From aa5bbe93bda0469673a4bba930a224e0aead847e Mon Sep 17 00:00:00 2001 From: d-millar <33498836+d-millar@users.noreply.github.com> Date: Wed, 2 Mar 2022 11:16:06 -0500 Subject: [PATCH] GP-1681: Adds basic Frida functionality to Ghidra in the form of a debugger model. --- .../garbage/AbstractModelForDbgTest.java | 1062 -------------- .../garbage/GadpForDbgTest.java | 125 -- .../garbage/ModelForDbgTest.java | 54 - .../Debug/Debugger-agent-frida/FridaNotes.txt | 27 + .../Debugger-agent-frida/Module.manifest | 0 .../Debug/Debugger-agent-frida/build.gradle | 107 ++ .../certification.manifest | 14 + .../data/scripts/onAccess.js | 5 + .../data/scripts/onAccessExt.js | 18 + .../data/scripts/onCallSummary.js | 1 + .../data/scripts/onEnter.js | 2 + .../data/scripts/onLeave.js | 2 + .../data/scripts/onReceive.js | 1 + .../ghidra_scripts/FridaTestScript.java | 24 + .../frida/FridaInJvmDebuggerModelFactory.java | 49 + .../java/agent/frida/frida/FridaClient.java | 392 +++++ .../agent/frida/frida/FridaClientImpl.java | 203 +++ .../frida/frida/FridaClientReentrant.java | 31 + .../main/java/agent/frida/frida/FridaEng.java | 332 +++++ .../agent/frida/frida/FridaModuleInfo.java | 84 ++ .../agent/frida/frida/FridaProcessInfo.java | 43 + .../agent/frida/frida/FridaRegionInfo.java | 76 + .../java/agent/frida/frida/FridaServerId.java | 58 + .../agent/frida/frida/FridaSessionInfo.java | 40 + .../agent/frida/frida/FridaThreadInfo.java | 41 + .../agent/frida/gadp/FridaGadpServer.java | 188 +++ .../gadp/FridaLocalDebuggerModelFactory.java | 78 + .../impl/AbstractClientThreadExecutor.java | 199 +++ .../gadp/impl/FridaClientThreadExecutor.java | 64 + .../frida/gadp/impl/FridaGadpServerImpl.java | 64 + .../java/agent/frida/jna/FridaNative.java | 285 ++++ .../agent/frida/manager/FridaApplication.java | 52 + .../java/agent/frida/manager/FridaCause.java | 33 + .../agent/frida/manager/FridaCommand.java | 82 ++ .../agent/frida/manager/FridaContext.java | 42 + .../agent/frida/manager/FridaDebugger.java | 26 + .../java/agent/frida/manager/FridaError.java | 45 + .../java/agent/frida/manager/FridaEvent.java | 63 + .../frida/manager/FridaEventsListener.java | 224 +++ .../manager/FridaEventsListenerAdapter.java | 146 ++ .../java/agent/frida/manager/FridaExport.java | 57 + .../agent/frida/manager/FridaFileSpec.java | 62 + .../java/agent/frida/manager/FridaFrame.java | 66 + .../agent/frida/manager/FridaFunction.java | 68 + .../java/agent/frida/manager/FridaImport.java | 75 + .../manager/FridaKernelMemoryRegionInfo.java | 36 + .../frida/manager/FridaKernelModule.java | 28 + .../agent/frida/manager/FridaManager.java | 396 +++++ .../frida/manager/FridaMemoryRegionInfo.java | 99 ++ .../java/agent/frida/manager/FridaModule.java | 66 + .../java/agent/frida/manager/FridaOS.java | 43 + .../agent/frida/manager/FridaPointer.java | 35 + .../agent/frida/manager/FridaProcess.java | 96 ++ .../java/agent/frida/manager/FridaReason.java | 44 + .../java/agent/frida/manager/FridaScript.java | 37 + .../agent/frida/manager/FridaSection.java | 98 ++ .../agent/frida/manager/FridaSession.java | 75 + .../java/agent/frida/manager/FridaState.java | 55 + .../frida/manager/FridaStateListener.java | 36 + .../java/agent/frida/manager/FridaSymbol.java | 84 ++ .../java/agent/frida/manager/FridaTarget.java | 80 + .../java/agent/frida/manager/FridaThread.java | 61 + .../java/agent/frida/manager/FridaValue.java | 47 + .../manager/cmd/AbstractFridaCommand.java | 159 ++ .../manager/cmd/FridaAddProcessCommand.java | 37 + .../manager/cmd/FridaAddSessionCommand.java | 37 + .../frida/manager/cmd/FridaAttachCommand.java | 91 ++ .../frida/manager/cmd/FridaCommandError.java | 59 + .../manager/cmd/FridaConsoleExecCommand.java | 79 + .../manager/cmd/FridaContinueCommand.java | 72 + .../manager/cmd/FridaDestroyCommand.java | 38 + .../frida/manager/cmd/FridaDetachCommand.java | 60 + .../cmd/FridaGetSessionAttributesCommand.java | 69 + .../cmd/FridaInterceptFunctionCommand.java | 70 + .../frida/manager/cmd/FridaKillCommand.java | 39 + .../cmd/FridaLaunchProcessCommand.java | 88 ++ .../FridaLaunchProcessWithOptionsCommand.java | 124 ++ .../FridaListAvailableProcessesCommand.java | 51 + .../FridaListHeapMemoryRegionsCommand.java | 63 + .../FridaListKernelMemoryRegionsCommand.java | 53 + .../cmd/FridaListKernelModulesCommand.java | 53 + .../cmd/FridaListMemoryRegionsCommand.java | 62 + .../cmd/FridaListModuleExportsCommand.java | 63 + .../cmd/FridaListModuleImportsCommand.java | 68 + .../cmd/FridaListModuleSectionsCommand.java | 69 + .../cmd/FridaListModuleSymbolsCommand.java | 70 + .../manager/cmd/FridaListModulesCommand.java | 57 + .../cmd/FridaListProcessesCommand.java | 63 + .../cmd/FridaListRegistersCommand.java | 45 + .../manager/cmd/FridaListSessionsCommand.java | 61 + .../cmd/FridaListStackFramesCommand.java | 62 + .../manager/cmd/FridaListThreadsCommand.java | 65 + .../manager/cmd/FridaPendingCommand.java | 211 +++ .../cmd/FridaReadKernelMemoryCommand.java | 73 + .../manager/cmd/FridaReadMemoryCommand.java | 73 + .../cmd/FridaRemoveProcessCommand.java | 35 + .../FridaRepeatableWatchMemoryCommand.java | 65 + .../cmd/FridaRequestActivationCommand.java | 46 + .../manager/cmd/FridaRequestFocusCommand.java | 45 + .../cmd/FridaSetExceptionHandlerCommand.java | 37 + .../manager/cmd/FridaStalkThreadCommand.java | 75 + .../manager/cmd/FridaWatchMemoryCommand.java | 68 + .../cmd/FridaWriteKernelMemoryCommand.java | 51 + .../manager/cmd/FridaWriteMemoryCommand.java | 51 + .../AbstractFridaCompletedCommandEvent.java | 34 + .../frida/manager/evt/AbstractFridaEvent.java | 84 ++ .../manager/evt/FridaCommandDoneEvent.java | 46 + .../manager/evt/FridaCommandErrorEvent.java | 45 + .../manager/evt/FridaConsoleOutputEvent.java | 41 + .../manager/evt/FridaInterruptEvent.java | 26 + .../evt/FridaMemoryRegionAddedEvent.java | 28 + .../evt/FridaMemoryRegionRemovedEvent.java | 28 + .../evt/FridaMemoryRegionReplacedEvent.java | 28 + .../manager/evt/FridaModuleLoadedEvent.java | 28 + .../manager/evt/FridaModuleReplacedEvent.java | 28 + .../manager/evt/FridaModuleUnloadedEvent.java | 28 + .../manager/evt/FridaProcessCreatedEvent.java | 32 + .../manager/evt/FridaProcessExitedEvent.java | 31 + .../evt/FridaProcessReplacedEvent.java | 38 + .../evt/FridaProcessSelectedEvent.java | 44 + .../frida/manager/evt/FridaRunningEvent.java | 49 + .../evt/FridaSelectedFrameChangedEvent.java | 62 + .../manager/evt/FridaSessionCreatedEvent.java | 25 + .../manager/evt/FridaSessionExitedEvent.java | 36 + .../evt/FridaSessionReplacedEvent.java | 26 + .../evt/FridaSessionSelectedEvent.java | 49 + .../manager/evt/FridaStateChangedEvent.java | 53 + .../frida/manager/evt/FridaStoppedEvent.java | 60 + .../manager/evt/FridaThreadCreatedEvent.java | 26 + .../manager/evt/FridaThreadExitedEvent.java | 24 + .../manager/evt/FridaThreadReplacedEvent.java | 26 + .../manager/evt/FridaThreadResumedEvent.java | 28 + .../manager/evt/FridaThreadSelectedEvent.java | 62 + .../evt/FridaThreadStackChangedEvent.java | 28 + .../evt/FridaThreadSuspendedEvent.java | 28 + .../frida/manager/impl/FridaManagerImpl.java | 1300 +++++++++++++++++ .../agent/frida/model/AbstractFridaModel.java | 45 + .../iface1/FridaModelSelectableObject.java | 35 + .../FridaModelTargetAccessConditioned.java | 36 + .../iface1/FridaModelTargetActiveScope.java | 68 + .../iface1/FridaModelTargetAttachable.java | 32 + .../iface1/FridaModelTargetAttacher.java | 40 + .../iface1/FridaModelTargetConfigurable.java | 28 + .../iface1/FridaModelTargetDeletable.java | 35 + .../iface1/FridaModelTargetDetachable.java | 35 + .../iface1/FridaModelTargetEnvironment.java | 38 + .../iface1/FridaModelTargetEventScope.java | 28 + .../FridaModelTargetExecutionStateful.java | 44 + .../iface1/FridaModelTargetFocusScope.java | 78 + .../iface1/FridaModelTargetInterpreter.java | 44 + .../iface1/FridaModelTargetKillable.java | 37 + .../iface1/FridaModelTargetLauncher.java | 36 + .../model/iface1/FridaModelTargetMethod.java | 28 + .../iface1/FridaModelTargetResumable.java | 35 + .../iface1/FridaModelTargetSteppable.java | 30 + .../iface2/FridaModelTargetAvailable.java | 27 + .../FridaModelTargetAvailableContainer.java | 22 + .../iface2/FridaModelTargetConnector.java | 41 + .../FridaModelTargetConnectorContainer.java | 22 + .../model/iface2/FridaModelTargetExport.java | 28 + .../FridaModelTargetExportContainer.java | 26 + .../iface2/FridaModelTargetFileSpec.java | 22 + .../iface2/FridaModelTargetFunction.java | 20 + .../model/iface2/FridaModelTargetImport.java | 29 + .../FridaModelTargetImportContainer.java | 26 + .../FridaModelTargetMemoryContainer.java | 42 + .../iface2/FridaModelTargetMemoryRegion.java | 35 + .../model/iface2/FridaModelTargetModule.java | 73 + .../FridaModelTargetModuleContainer.java | 42 + .../iface2/FridaModelTargetModuleSection.java | 26 + ...ridaModelTargetModuleSectionContainer.java | 23 + .../model/iface2/FridaModelTargetObject.java | 87 ++ .../model/iface2/FridaModelTargetProcess.java | 55 + .../FridaModelTargetProcessContainer.java | 28 + .../iface2/FridaModelTargetRegister.java | 37 + .../iface2/FridaModelTargetRegisterBank.java | 61 + .../FridaModelTargetRegisterContainer.java | 23 + ...daModelTargetRegisterContainerAndBank.java | 31 + .../model/iface2/FridaModelTargetRoot.java | 35 + .../model/iface2/FridaModelTargetSession.java | 63 + .../FridaModelTargetSessionAttributes.java | 25 + ...delTargetSessionAttributesEnvironment.java | 24 + ...aModelTargetSessionAttributesPlatform.java | 24 + .../FridaModelTargetSessionContainer.java | 36 + .../model/iface2/FridaModelTargetStack.java | 26 + .../iface2/FridaModelTargetStackFrame.java | 102 ++ .../FridaModelTargetStackFrameRegister.java | 50 + ...ridaModelTargetStackFrameRegisterBank.java | 40 + ...odelTargetStackFrameRegisterContainer.java | 47 + .../model/iface2/FridaModelTargetSymbol.java | 31 + .../FridaModelTargetSymbolContainer.java | 27 + .../model/iface2/FridaModelTargetThread.java | 57 + .../FridaModelTargetThreadContainer.java | 33 + .../frida/model/impl/FridaDebuggerBot.java | 201 +++ .../FridaModelDefaultTargetModelRoot.java | 33 + .../frida/model/impl/FridaModelImpl.java | 185 +++ ...ridaModelTargetAvailableContainerImpl.java | 104 ++ .../impl/FridaModelTargetAvailableImpl.java | 91 ++ ...ridaModelTargetConnectorContainerImpl.java | 87 ++ .../FridaModelTargetExportContainerImpl.java | 73 + .../impl/FridaModelTargetExportImpl.java | 101 ++ .../impl/FridaModelTargetFileSpecImpl.java | 62 + .../impl/FridaModelTargetFunctionImpl.java | 64 + ...idaModelTargetHeapMemoryContainerImpl.java | 45 + .../FridaModelTargetImportContainerImpl.java | 73 + .../impl/FridaModelTargetImportImpl.java | 113 ++ .../impl/FridaModelTargetKernelImpl.java | 70 + ...aModelTargetKernelMemoryContainerImpl.java | 201 +++ ...aModelTargetKernelModuleContainerImpl.java | 131 ++ .../FridaModelTargetKernelModuleImpl.java | 103 ++ .../FridaModelTargetMemoryContainerImpl.java | 235 +++ .../FridaModelTargetMemoryRegionImpl.java | 136 ++ .../FridaModelTargetModuleContainerImpl.java | 162 ++ .../impl/FridaModelTargetModuleImpl.java | 146 ++ ...ModelTargetModuleSectionContainerImpl.java | 107 ++ .../FridaModelTargetModuleSectionImpl.java | 99 ++ .../impl/FridaModelTargetObjectImpl.java | 264 ++++ ...TargetProcessAttachByPidConnectorImpl.java | 86 ++ .../FridaModelTargetProcessContainerImpl.java | 149 ++ .../impl/FridaModelTargetProcessImpl.java | 269 ++++ ...ModelTargetProcessLaunchConnectorImpl.java | 97 ++ ...ProcessLaunchWithOptionsConnectorImpl.java | 160 ++ ...FridaModelTargetRegisterContainerImpl.java | 165 +++ .../impl/FridaModelTargetRegisterImpl.java | 120 ++ .../model/impl/FridaModelTargetRootImpl.java | 180 +++ ...argetSessionAttributesEnvironmentImpl.java | 58 + ...FridaModelTargetSessionAttributesImpl.java | 70 + ...elTargetSessionAttributesPlatformImpl.java | 58 + .../FridaModelTargetSessionContainerImpl.java | 93 ++ .../impl/FridaModelTargetSessionImpl.java | 147 ++ .../impl/FridaModelTargetStackFrameImpl.java | 182 +++ .../model/impl/FridaModelTargetStackImpl.java | 100 ++ .../FridaModelTargetSymbolContainerImpl.java | 87 ++ .../impl/FridaModelTargetSymbolImpl.java | 122 ++ .../FridaModelTargetThreadContainerImpl.java | 184 +++ .../impl/FridaModelTargetThreadImpl.java | 196 +++ ...idaModelTargetFunctionInterceptorImpl.java | 94 ++ .../FridaModelTargetMemoryPatchImpl.java | 78 + .../FridaModelTargetMemoryProtectImpl.java | 77 + .../FridaModelTargetMemoryScanImpl.java | 100 ++ .../FridaModelTargetMemoryWatchImpl.java | 81 + .../FridaModelTargetModuleInitImpl.java | 66 + ...FridaModelTargetModuleInterceptorImpl.java | 85 ++ .../FridaModelTargetModuleLoadImpl.java | 66 + ...FridaModelTargetSymbolFromAddressImpl.java | 70 + .../FridaModelTargetSymbolFromNameImpl.java | 67 + .../FridaModelTargetSymbolLoadImpl.java | 66 + .../FridaModelTargetThreadSleepImpl.java | 66 + .../FridaModelTargetThreadStalkImpl.java | 120 ++ .../FridaModelTargetUnloadScriptImpl.java | 71 + .../frida/model/AbstractFridaModelHost.java | 27 + .../AbstractModelForFridaActivationTest.java | 113 ++ .../AbstractModelForFridaFactoryTest.java | 27 + .../AbstractModelForFridaInterpreterTest.java | 84 ++ .../AbstractModelForFridaMethodsTest.java | 296 ++++ ...AbstractModelForFridaRootAttacherTest.java | 102 ++ ...AbstractModelForFridaRootLauncherTest.java | 81 + ...bstractModelForFridaScenarioStackTest.java | 127 ++ ...AbstractModelForFridaX64RegistersTest.java | 169 +++ .../agent/frida/model/FridaLinuxSpecimen.java | 126 ++ .../frida/model/gadp/GadpFridaModelHost.java | 31 + .../gadp/GadpModelForFridaFactoryTest.java | 25 + .../GadpModelForFridaInterpreterTest.java | 64 + .../GadpModelForFridaRootLauncherTest.java | 25 + .../GadpModelForFridaScenarioStackTest.java | 25 + .../GadpModelForFridaX64RegistersTest.java | 27 + .../frida/model/invm/InVmFridaModelHost.java | 27 + .../invm/InVmModelForFridaFactoryTest.java | 25 + .../InVmModelForFridaInterpreterTest.java | 64 + .../invm/InVmModelForFridaMethodsTest.java | 25 + .../InVmModelForFridaRootAttacherTest.java | 40 + .../InVmModelForFridaRootLauncherTest.java | 25 + .../InVmModelForFridaScenarioStackTest.java | 25 + .../InVmModelForFridaX64RegistersTest.java | 27 + .../gdb/model/impl/GdbModelTargetThread.java | 1 + .../impl/GdbModelTargetThreadContainer.java | 8 +- .../model/impl/LldbModelTargetThreadImpl.java | 1 + .../gui/objects/DebuggerObjectsProvider.java | 268 +++- .../arm/FridaArmDebuggerMappingOpinion.java | 74 + .../FridaDebuggerProgramLaunchOpinion.java | 118 ++ .../frida/FridaX86DebuggerMappingOpinion.java | 126 ++ .../java/ghidra/dbg/target/TargetMethod.java | 2 + 282 files changed, 21013 insertions(+), 1260 deletions(-) delete mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/garbage/AbstractModelForDbgTest.java delete mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/garbage/GadpForDbgTest.java delete mode 100644 Ghidra/Debug/Debugger-agent-dbgeng/garbage/ModelForDbgTest.java create mode 100644 Ghidra/Debug/Debugger-agent-frida/FridaNotes.txt create mode 100644 Ghidra/Debug/Debugger-agent-frida/Module.manifest create mode 100644 Ghidra/Debug/Debugger-agent-frida/build.gradle create mode 100644 Ghidra/Debug/Debugger-agent-frida/certification.manifest create mode 100644 Ghidra/Debug/Debugger-agent-frida/data/scripts/onAccess.js create mode 100644 Ghidra/Debug/Debugger-agent-frida/data/scripts/onAccessExt.js create mode 100644 Ghidra/Debug/Debugger-agent-frida/data/scripts/onCallSummary.js create mode 100644 Ghidra/Debug/Debugger-agent-frida/data/scripts/onEnter.js create mode 100644 Ghidra/Debug/Debugger-agent-frida/data/scripts/onLeave.js create mode 100644 Ghidra/Debug/Debugger-agent-frida/data/scripts/onReceive.js create mode 100644 Ghidra/Debug/Debugger-agent-frida/ghidra_scripts/FridaTestScript.java create mode 100644 Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/FridaInJvmDebuggerModelFactory.java create mode 100644 Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/frida/FridaClient.java create mode 100644 Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/frida/FridaClientImpl.java create mode 100644 Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/frida/FridaClientReentrant.java create mode 100644 Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/frida/FridaEng.java create mode 100644 Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/frida/FridaModuleInfo.java create mode 100644 Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/frida/FridaProcessInfo.java create mode 100644 Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/frida/FridaRegionInfo.java create mode 100644 Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/frida/FridaServerId.java create mode 100644 Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/frida/FridaSessionInfo.java create mode 100644 Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/frida/FridaThreadInfo.java create mode 100644 Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/gadp/FridaGadpServer.java create mode 100644 Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/gadp/FridaLocalDebuggerModelFactory.java create mode 100644 Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/gadp/impl/AbstractClientThreadExecutor.java create mode 100644 Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/gadp/impl/FridaClientThreadExecutor.java create mode 100644 Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/gadp/impl/FridaGadpServerImpl.java create mode 100644 Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/jna/FridaNative.java create mode 100644 Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/FridaApplication.java create mode 100644 Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/FridaCause.java create mode 100644 Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/FridaCommand.java create mode 100644 Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/FridaContext.java create mode 100644 Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/FridaDebugger.java create mode 100644 Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/FridaError.java create mode 100644 Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/FridaEvent.java create mode 100644 Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/FridaEventsListener.java create mode 100644 Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/FridaEventsListenerAdapter.java create mode 100644 Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/FridaExport.java create mode 100644 Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/FridaFileSpec.java create mode 100644 Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/FridaFrame.java create mode 100644 Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/FridaFunction.java create mode 100644 Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/FridaImport.java create mode 100644 Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/FridaKernelMemoryRegionInfo.java create mode 100644 Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/FridaKernelModule.java create mode 100644 Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/FridaManager.java create mode 100644 Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/FridaMemoryRegionInfo.java create mode 100644 Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/FridaModule.java create mode 100644 Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/FridaOS.java create mode 100644 Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/FridaPointer.java create mode 100644 Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/FridaProcess.java create mode 100644 Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/FridaReason.java create mode 100644 Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/FridaScript.java create mode 100644 Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/FridaSection.java create mode 100644 Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/FridaSession.java create mode 100644 Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/FridaState.java create mode 100644 Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/FridaStateListener.java create mode 100644 Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/FridaSymbol.java create mode 100644 Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/FridaTarget.java create mode 100644 Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/FridaThread.java create mode 100644 Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/FridaValue.java create mode 100644 Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/cmd/AbstractFridaCommand.java create mode 100644 Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/cmd/FridaAddProcessCommand.java create mode 100644 Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/cmd/FridaAddSessionCommand.java create mode 100644 Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/cmd/FridaAttachCommand.java create mode 100644 Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/cmd/FridaCommandError.java create mode 100644 Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/cmd/FridaConsoleExecCommand.java create mode 100644 Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/cmd/FridaContinueCommand.java create mode 100644 Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/cmd/FridaDestroyCommand.java create mode 100644 Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/cmd/FridaDetachCommand.java create mode 100644 Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/cmd/FridaGetSessionAttributesCommand.java create mode 100644 Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/cmd/FridaInterceptFunctionCommand.java create mode 100644 Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/cmd/FridaKillCommand.java create mode 100644 Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/cmd/FridaLaunchProcessCommand.java create mode 100644 Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/cmd/FridaLaunchProcessWithOptionsCommand.java create mode 100644 Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/cmd/FridaListAvailableProcessesCommand.java create mode 100644 Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/cmd/FridaListHeapMemoryRegionsCommand.java create mode 100644 Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/cmd/FridaListKernelMemoryRegionsCommand.java create mode 100644 Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/cmd/FridaListKernelModulesCommand.java create mode 100644 Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/cmd/FridaListMemoryRegionsCommand.java create mode 100644 Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/cmd/FridaListModuleExportsCommand.java create mode 100644 Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/cmd/FridaListModuleImportsCommand.java create mode 100644 Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/cmd/FridaListModuleSectionsCommand.java create mode 100644 Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/cmd/FridaListModuleSymbolsCommand.java create mode 100644 Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/cmd/FridaListModulesCommand.java create mode 100644 Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/cmd/FridaListProcessesCommand.java create mode 100644 Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/cmd/FridaListRegistersCommand.java create mode 100644 Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/cmd/FridaListSessionsCommand.java create mode 100644 Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/cmd/FridaListStackFramesCommand.java create mode 100644 Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/cmd/FridaListThreadsCommand.java create mode 100644 Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/cmd/FridaPendingCommand.java create mode 100644 Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/cmd/FridaReadKernelMemoryCommand.java create mode 100644 Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/cmd/FridaReadMemoryCommand.java create mode 100644 Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/cmd/FridaRemoveProcessCommand.java create mode 100644 Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/cmd/FridaRepeatableWatchMemoryCommand.java create mode 100644 Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/cmd/FridaRequestActivationCommand.java create mode 100644 Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/cmd/FridaRequestFocusCommand.java create mode 100644 Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/cmd/FridaSetExceptionHandlerCommand.java create mode 100644 Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/cmd/FridaStalkThreadCommand.java create mode 100644 Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/cmd/FridaWatchMemoryCommand.java create mode 100644 Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/cmd/FridaWriteKernelMemoryCommand.java create mode 100644 Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/cmd/FridaWriteMemoryCommand.java create mode 100644 Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/evt/AbstractFridaCompletedCommandEvent.java create mode 100644 Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/evt/AbstractFridaEvent.java create mode 100644 Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/evt/FridaCommandDoneEvent.java create mode 100644 Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/evt/FridaCommandErrorEvent.java create mode 100644 Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/evt/FridaConsoleOutputEvent.java create mode 100644 Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/evt/FridaInterruptEvent.java create mode 100644 Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/evt/FridaMemoryRegionAddedEvent.java create mode 100644 Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/evt/FridaMemoryRegionRemovedEvent.java create mode 100644 Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/evt/FridaMemoryRegionReplacedEvent.java create mode 100644 Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/evt/FridaModuleLoadedEvent.java create mode 100644 Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/evt/FridaModuleReplacedEvent.java create mode 100644 Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/evt/FridaModuleUnloadedEvent.java create mode 100644 Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/evt/FridaProcessCreatedEvent.java create mode 100644 Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/evt/FridaProcessExitedEvent.java create mode 100644 Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/evt/FridaProcessReplacedEvent.java create mode 100644 Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/evt/FridaProcessSelectedEvent.java create mode 100644 Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/evt/FridaRunningEvent.java create mode 100644 Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/evt/FridaSelectedFrameChangedEvent.java create mode 100644 Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/evt/FridaSessionCreatedEvent.java create mode 100644 Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/evt/FridaSessionExitedEvent.java create mode 100644 Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/evt/FridaSessionReplacedEvent.java create mode 100644 Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/evt/FridaSessionSelectedEvent.java create mode 100644 Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/evt/FridaStateChangedEvent.java create mode 100644 Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/evt/FridaStoppedEvent.java create mode 100644 Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/evt/FridaThreadCreatedEvent.java create mode 100644 Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/evt/FridaThreadExitedEvent.java create mode 100644 Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/evt/FridaThreadReplacedEvent.java create mode 100644 Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/evt/FridaThreadResumedEvent.java create mode 100644 Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/evt/FridaThreadSelectedEvent.java create mode 100644 Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/evt/FridaThreadStackChangedEvent.java create mode 100644 Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/evt/FridaThreadSuspendedEvent.java create mode 100644 Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/impl/FridaManagerImpl.java create mode 100644 Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/AbstractFridaModel.java create mode 100644 Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/iface1/FridaModelSelectableObject.java create mode 100644 Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/iface1/FridaModelTargetAccessConditioned.java create mode 100644 Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/iface1/FridaModelTargetActiveScope.java create mode 100644 Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/iface1/FridaModelTargetAttachable.java create mode 100644 Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/iface1/FridaModelTargetAttacher.java create mode 100644 Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/iface1/FridaModelTargetConfigurable.java create mode 100644 Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/iface1/FridaModelTargetDeletable.java create mode 100644 Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/iface1/FridaModelTargetDetachable.java create mode 100644 Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/iface1/FridaModelTargetEnvironment.java create mode 100644 Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/iface1/FridaModelTargetEventScope.java create mode 100644 Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/iface1/FridaModelTargetExecutionStateful.java create mode 100644 Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/iface1/FridaModelTargetFocusScope.java create mode 100644 Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/iface1/FridaModelTargetInterpreter.java create mode 100644 Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/iface1/FridaModelTargetKillable.java create mode 100644 Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/iface1/FridaModelTargetLauncher.java create mode 100644 Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/iface1/FridaModelTargetMethod.java create mode 100644 Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/iface1/FridaModelTargetResumable.java create mode 100644 Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/iface1/FridaModelTargetSteppable.java create mode 100644 Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/iface2/FridaModelTargetAvailable.java create mode 100644 Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/iface2/FridaModelTargetAvailableContainer.java create mode 100644 Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/iface2/FridaModelTargetConnector.java create mode 100644 Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/iface2/FridaModelTargetConnectorContainer.java create mode 100644 Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/iface2/FridaModelTargetExport.java create mode 100644 Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/iface2/FridaModelTargetExportContainer.java create mode 100644 Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/iface2/FridaModelTargetFileSpec.java create mode 100644 Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/iface2/FridaModelTargetFunction.java create mode 100644 Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/iface2/FridaModelTargetImport.java create mode 100644 Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/iface2/FridaModelTargetImportContainer.java create mode 100644 Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/iface2/FridaModelTargetMemoryContainer.java create mode 100644 Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/iface2/FridaModelTargetMemoryRegion.java create mode 100644 Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/iface2/FridaModelTargetModule.java create mode 100644 Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/iface2/FridaModelTargetModuleContainer.java create mode 100644 Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/iface2/FridaModelTargetModuleSection.java create mode 100644 Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/iface2/FridaModelTargetModuleSectionContainer.java create mode 100644 Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/iface2/FridaModelTargetObject.java create mode 100644 Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/iface2/FridaModelTargetProcess.java create mode 100644 Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/iface2/FridaModelTargetProcessContainer.java create mode 100644 Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/iface2/FridaModelTargetRegister.java create mode 100644 Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/iface2/FridaModelTargetRegisterBank.java create mode 100644 Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/iface2/FridaModelTargetRegisterContainer.java create mode 100644 Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/iface2/FridaModelTargetRegisterContainerAndBank.java create mode 100644 Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/iface2/FridaModelTargetRoot.java create mode 100644 Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/iface2/FridaModelTargetSession.java create mode 100644 Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/iface2/FridaModelTargetSessionAttributes.java create mode 100644 Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/iface2/FridaModelTargetSessionAttributesEnvironment.java create mode 100644 Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/iface2/FridaModelTargetSessionAttributesPlatform.java create mode 100644 Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/iface2/FridaModelTargetSessionContainer.java create mode 100644 Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/iface2/FridaModelTargetStack.java create mode 100644 Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/iface2/FridaModelTargetStackFrame.java create mode 100644 Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/iface2/FridaModelTargetStackFrameRegister.java create mode 100644 Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/iface2/FridaModelTargetStackFrameRegisterBank.java create mode 100644 Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/iface2/FridaModelTargetStackFrameRegisterContainer.java create mode 100644 Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/iface2/FridaModelTargetSymbol.java create mode 100644 Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/iface2/FridaModelTargetSymbolContainer.java create mode 100644 Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/iface2/FridaModelTargetThread.java create mode 100644 Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/iface2/FridaModelTargetThreadContainer.java create mode 100644 Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/impl/FridaDebuggerBot.java create mode 100644 Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/impl/FridaModelDefaultTargetModelRoot.java create mode 100644 Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/impl/FridaModelImpl.java create mode 100644 Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/impl/FridaModelTargetAvailableContainerImpl.java create mode 100644 Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/impl/FridaModelTargetAvailableImpl.java create mode 100644 Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/impl/FridaModelTargetConnectorContainerImpl.java create mode 100644 Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/impl/FridaModelTargetExportContainerImpl.java create mode 100644 Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/impl/FridaModelTargetExportImpl.java create mode 100644 Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/impl/FridaModelTargetFileSpecImpl.java create mode 100644 Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/impl/FridaModelTargetFunctionImpl.java create mode 100644 Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/impl/FridaModelTargetHeapMemoryContainerImpl.java create mode 100644 Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/impl/FridaModelTargetImportContainerImpl.java create mode 100644 Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/impl/FridaModelTargetImportImpl.java create mode 100644 Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/impl/FridaModelTargetKernelImpl.java create mode 100644 Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/impl/FridaModelTargetKernelMemoryContainerImpl.java create mode 100644 Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/impl/FridaModelTargetKernelModuleContainerImpl.java create mode 100644 Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/impl/FridaModelTargetKernelModuleImpl.java create mode 100644 Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/impl/FridaModelTargetMemoryContainerImpl.java create mode 100644 Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/impl/FridaModelTargetMemoryRegionImpl.java create mode 100644 Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/impl/FridaModelTargetModuleContainerImpl.java create mode 100644 Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/impl/FridaModelTargetModuleImpl.java create mode 100644 Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/impl/FridaModelTargetModuleSectionContainerImpl.java create mode 100644 Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/impl/FridaModelTargetModuleSectionImpl.java create mode 100644 Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/impl/FridaModelTargetObjectImpl.java create mode 100644 Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/impl/FridaModelTargetProcessAttachByPidConnectorImpl.java create mode 100644 Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/impl/FridaModelTargetProcessContainerImpl.java create mode 100644 Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/impl/FridaModelTargetProcessImpl.java create mode 100644 Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/impl/FridaModelTargetProcessLaunchConnectorImpl.java create mode 100644 Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/impl/FridaModelTargetProcessLaunchWithOptionsConnectorImpl.java create mode 100644 Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/impl/FridaModelTargetRegisterContainerImpl.java create mode 100644 Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/impl/FridaModelTargetRegisterImpl.java create mode 100644 Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/impl/FridaModelTargetRootImpl.java create mode 100644 Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/impl/FridaModelTargetSessionAttributesEnvironmentImpl.java create mode 100644 Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/impl/FridaModelTargetSessionAttributesImpl.java create mode 100644 Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/impl/FridaModelTargetSessionAttributesPlatformImpl.java create mode 100644 Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/impl/FridaModelTargetSessionContainerImpl.java create mode 100644 Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/impl/FridaModelTargetSessionImpl.java create mode 100644 Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/impl/FridaModelTargetStackFrameImpl.java create mode 100644 Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/impl/FridaModelTargetStackImpl.java create mode 100644 Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/impl/FridaModelTargetSymbolContainerImpl.java create mode 100644 Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/impl/FridaModelTargetSymbolImpl.java create mode 100644 Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/impl/FridaModelTargetThreadContainerImpl.java create mode 100644 Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/impl/FridaModelTargetThreadImpl.java create mode 100644 Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/methods/FridaModelTargetFunctionInterceptorImpl.java create mode 100644 Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/methods/FridaModelTargetMemoryPatchImpl.java create mode 100644 Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/methods/FridaModelTargetMemoryProtectImpl.java create mode 100644 Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/methods/FridaModelTargetMemoryScanImpl.java create mode 100644 Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/methods/FridaModelTargetMemoryWatchImpl.java create mode 100644 Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/methods/FridaModelTargetModuleInitImpl.java create mode 100644 Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/methods/FridaModelTargetModuleInterceptorImpl.java create mode 100644 Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/methods/FridaModelTargetModuleLoadImpl.java create mode 100644 Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/methods/FridaModelTargetSymbolFromAddressImpl.java create mode 100644 Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/methods/FridaModelTargetSymbolFromNameImpl.java create mode 100644 Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/methods/FridaModelTargetSymbolLoadImpl.java create mode 100644 Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/methods/FridaModelTargetThreadSleepImpl.java create mode 100644 Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/methods/FridaModelTargetThreadStalkImpl.java create mode 100644 Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/methods/FridaModelTargetUnloadScriptImpl.java create mode 100644 Ghidra/Debug/Debugger-agent-frida/src/test/java/agent/frida/model/AbstractFridaModelHost.java create mode 100644 Ghidra/Debug/Debugger-agent-frida/src/test/java/agent/frida/model/AbstractModelForFridaActivationTest.java create mode 100644 Ghidra/Debug/Debugger-agent-frida/src/test/java/agent/frida/model/AbstractModelForFridaFactoryTest.java create mode 100644 Ghidra/Debug/Debugger-agent-frida/src/test/java/agent/frida/model/AbstractModelForFridaInterpreterTest.java create mode 100644 Ghidra/Debug/Debugger-agent-frida/src/test/java/agent/frida/model/AbstractModelForFridaMethodsTest.java create mode 100644 Ghidra/Debug/Debugger-agent-frida/src/test/java/agent/frida/model/AbstractModelForFridaRootAttacherTest.java create mode 100644 Ghidra/Debug/Debugger-agent-frida/src/test/java/agent/frida/model/AbstractModelForFridaRootLauncherTest.java create mode 100644 Ghidra/Debug/Debugger-agent-frida/src/test/java/agent/frida/model/AbstractModelForFridaScenarioStackTest.java create mode 100644 Ghidra/Debug/Debugger-agent-frida/src/test/java/agent/frida/model/AbstractModelForFridaX64RegistersTest.java create mode 100644 Ghidra/Debug/Debugger-agent-frida/src/test/java/agent/frida/model/FridaLinuxSpecimen.java create mode 100644 Ghidra/Debug/Debugger-agent-frida/src/test/java/agent/frida/model/gadp/GadpFridaModelHost.java create mode 100644 Ghidra/Debug/Debugger-agent-frida/src/test/java/agent/frida/model/gadp/GadpModelForFridaFactoryTest.java create mode 100644 Ghidra/Debug/Debugger-agent-frida/src/test/java/agent/frida/model/gadp/GadpModelForFridaInterpreterTest.java create mode 100644 Ghidra/Debug/Debugger-agent-frida/src/test/java/agent/frida/model/gadp/GadpModelForFridaRootLauncherTest.java create mode 100644 Ghidra/Debug/Debugger-agent-frida/src/test/java/agent/frida/model/gadp/GadpModelForFridaScenarioStackTest.java create mode 100644 Ghidra/Debug/Debugger-agent-frida/src/test/java/agent/frida/model/gadp/GadpModelForFridaX64RegistersTest.java create mode 100644 Ghidra/Debug/Debugger-agent-frida/src/test/java/agent/frida/model/invm/InVmFridaModelHost.java create mode 100644 Ghidra/Debug/Debugger-agent-frida/src/test/java/agent/frida/model/invm/InVmModelForFridaFactoryTest.java create mode 100644 Ghidra/Debug/Debugger-agent-frida/src/test/java/agent/frida/model/invm/InVmModelForFridaInterpreterTest.java create mode 100644 Ghidra/Debug/Debugger-agent-frida/src/test/java/agent/frida/model/invm/InVmModelForFridaMethodsTest.java create mode 100644 Ghidra/Debug/Debugger-agent-frida/src/test/java/agent/frida/model/invm/InVmModelForFridaRootAttacherTest.java create mode 100644 Ghidra/Debug/Debugger-agent-frida/src/test/java/agent/frida/model/invm/InVmModelForFridaRootLauncherTest.java create mode 100644 Ghidra/Debug/Debugger-agent-frida/src/test/java/agent/frida/model/invm/InVmModelForFridaScenarioStackTest.java create mode 100644 Ghidra/Debug/Debugger-agent-frida/src/test/java/agent/frida/model/invm/InVmModelForFridaX64RegistersTest.java create mode 100644 Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/platform/arm/FridaArmDebuggerMappingOpinion.java create mode 100644 Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/platform/frida/FridaDebuggerProgramLaunchOpinion.java create mode 100644 Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/platform/frida/FridaX86DebuggerMappingOpinion.java diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/garbage/AbstractModelForDbgTest.java b/Ghidra/Debug/Debugger-agent-dbgeng/garbage/AbstractModelForDbgTest.java deleted file mode 100644 index 6a04551ff4..0000000000 --- a/Ghidra/Debug/Debugger-agent-dbgeng/garbage/AbstractModelForDbgTest.java +++ /dev/null @@ -1,1062 +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 agent.dbgeng.testutil.DummyProc.runProc; -import static ghidra.lifecycle.Unfinished.TODO; -import static org.junit.Assert.*; - -import java.lang.invoke.MethodHandles; -import java.util.*; -import java.util.Map.Entry; -import java.util.concurrent.*; -import java.util.concurrent.atomic.AtomicReference; -import java.util.stream.Collectors; - -import org.junit.*; - -import agent.dbgeng.dbgeng.DbgEngTest; -import agent.dbgeng.model.iface1.DbgModelTargetLauncher; -import agent.dbgeng.testutil.DummyProc; -import ghidra.async.*; -import ghidra.dbg.*; -import ghidra.dbg.DebugModelConventions.AllRequiredAccess; -import ghidra.dbg.error.DebuggerModelNoSuchPathException; -import ghidra.dbg.error.DebuggerModelTypeException; -import ghidra.dbg.target.*; -import ghidra.dbg.target.TargetBreakpointSpecContainer.TargetBreakpointKindSet; -import ghidra.dbg.target.TargetBreakpointSpec.TargetBreakpointKind; -import ghidra.dbg.target.TargetConsole.Channel; -import ghidra.dbg.target.TargetLauncher.TargetCmdLineLauncher; -import ghidra.dbg.target.schema.TargetObjectSchema; -import ghidra.dbg.target.schema.XmlSchemaContext; -import ghidra.dbg.util.PathUtils; -import ghidra.program.model.address.Address; -import ghidra.test.AbstractGhidraHeadlessIntegrationTest; -import ghidra.util.*; - -public abstract class AbstractModelForDbgTest - extends AbstractGhidraHeadlessIntegrationTest { - protected static final Map AMD64_TEST_REG_VALUES = Map.of( // - "rax", NumericUtilities.convertStringToBytes("0123456789abcdef"), // - "ymm0", NumericUtilities.convertStringToBytes("" + // - "0011223344556677" + "8899aabbccddeeff")); -// TODO: for some reason, the dbgeng thinks ymm0 is VECTOR128 -//"0123456789abcdef" + "fedcba9876543210" + "0011223344556677" + "8899aabbccddeeff")); - protected static final long TIMEOUT_MILLISECONDS = - SystemUtilities.isInTestingBatchMode() ? 5000 : Long.MAX_VALUE; - - protected static Map hexlify(Map map) { - return map.entrySet() - .stream() - .collect(Collectors.toMap(Entry::getKey, - e -> NumericUtilities.convertBytesToString(e.getValue()))); - } - - public interface ModelHost extends AutoCloseable { - DebuggerObjectModel getModel(); - - CompletableFuture init(); - } - - protected abstract ModelHost modelHost() throws Exception; - - protected static class CatchOffThread implements AutoCloseable { - protected Throwable caught; - - void catching(Runnable runnable) { - try { - runnable.run(); - } - catch (Throwable e) { - caught = e; - } - } - - @Override - public void close() throws Exception { - if (caught != null) { - throw new AssertionError("Off-thread exception", caught); - } - } - } - - protected static T waitOn(CompletableFuture future) throws Throwable { - try { - return future.get(TIMEOUT_MILLISECONDS, TimeUnit.MILLISECONDS); - } - catch (ExecutionException e) { - throw e.getCause(); - } - } - - @Before - public void setUpDbgEngTest() { - DbgEngTest.assumeDbgengDLLLoadable(); - } - - @Test - public void testInitFinish() throws Throwable { - try (ModelHost m = modelHost()) { - waitOn(AsyncUtils.sequence(TypeSpec.VOID).then(seq -> { - m.init().handle(seq::next); - }).finish()); - } - } - - @Test - public void testNonExistentPathGivesNull() throws Throwable { - try (ModelHost m = modelHost()) { - DebuggerObjectModel model = m.getModel(); - - AtomicReference root = new AtomicReference<>(); - AtomicReference access = new AtomicReference<>(); - waitOn(AsyncUtils.sequence(TypeSpec.VOID).then(seq -> { - m.init().handle(seq::next); - }).then(seq -> { - Msg.debug(this, "Getting session root object"); - model.fetchModelObject("Sessions", "[0]").handle(seq::next); - }, root).then(seq -> { - Msg.debug(this, "Tracking session access..."); - DebugModelConventions.trackAccessibility(root.get()).handle(seq::next); - }, access).then(seq -> { - Msg.debug(this, "Waiting for session access..."); - access.get().waitValue(true).handle(seq::next); - }).then(seq -> { - model.fetchModelObject("Doesn't exist").handle(seq::next); - }, TypeSpec.cls(TargetObject.class)).then((obj, seq) -> { - assertNull(obj); - seq.exit(); - }).finish()); - } - } - - @Test - public void testSessionLaunch() throws Throwable { - try (ModelHost m = modelHost()) { - DebuggerObjectModel model = m.getModel(); - - AtomicReference root = new AtomicReference<>(); - AtomicReference access = new AtomicReference<>(); - AtomicReference launcher = new AtomicReference<>(); - TypeSpec> t = TypeSpec.auto(); - - waitOn(AsyncUtils.sequence(TypeSpec.VOID).then(seq -> { - m.init().handle(seq::next); - }).then(seq -> { - Msg.debug(this, "Getting session root object"); - model.fetchModelObject("Sessions", "[0]").handle(seq::next); - }, root).then(seq -> { - Msg.debug(this, "Tracking session access..."); - DebugModelConventions.trackAccessibility(root.get()).handle(seq::next); - }, access).then(seq -> { - Msg.debug(this, "Waiting for session access..."); - access.get().waitValue(true).handle(seq::next); - }).then(seq -> { - Msg.debug(this, "Finding TargetLauncher..."); - DebugModelConventions.findSuitable(DbgModelTargetLauncher.class, root.get()) - .handle(seq::next); - }, launcher).then(seq -> { - Msg.debug(this, "Launching..."); - launcher.get().launch("notepad", "junk.txt").handle(seq::nextIgnore); - }).then(seq -> { - Msg.debug(this, "Waiting for session access..."); - access.get().waitValue(true).handle(seq::next); - }).then(seq -> { - Msg.debug(this, "Getting Processes (after launch)..."); - model.fetchObjectElements(List.of("Sessions", "[0]")) - .handle(seq::next); - }, t).then((children, seq) -> { - Msg.debug(this, "Processes after: " + children); - assertEquals(1, children.size()); - seq.exit(); - }).finish()); - } - } - - @Test - public void testProcessLaunch() throws Throwable { - try (ModelHost m = modelHost()) { - DebuggerObjectModel model = m.getModel(); - - AtomicReference root = new AtomicReference<>(); - AtomicReference session = new AtomicReference<>(); - AtomicReference rootAccess = new AtomicReference<>(); - AtomicReference launcher = new AtomicReference<>(); - AtomicReference launcherAccess = new AtomicReference<>(); - TypeSpec> t = TypeSpec.auto(); - - waitOn(AsyncUtils.sequence(TypeSpec.VOID).then(seq -> { - m.init().handle(seq::next); - }).then(seq -> { - Msg.debug(this, "Getting session root object"); - model.fetchModelObject("Sessions", "[0]").handle(seq::next); - }, root).then(seq -> { - Msg.debug(this, "Tracking session access..."); - DebugModelConventions.trackAccessibility(root.get()).handle(seq::next); - }, rootAccess).then(seq -> { - Msg.debug(this, "Waiting for session access..."); - rootAccess.get().waitValue(true).handle(seq::next); - }).then(seq -> { - model.fetchModelObject(List.of("Sessions", "[0]")).handle(seq::next); - }, session).then(seq -> { - Msg.debug(this, "Finding TargetLauncher..."); - DebugModelConventions.findSuitable(DbgModelTargetLauncher.class, - session.get()).handle(seq::next); - }, TypeSpec.cls(TargetObject.class)).then((obj, seq) -> { - assertTrue(obj.getInterfaceNames().contains("Launcher")); - launcher.set((DbgModelTargetLauncher) obj); - Msg.debug(this, "Tracking process access..."); - DebugModelConventions.trackAccessibility(obj).handle(seq::next); - }, launcherAccess).then(seq -> { - Msg.debug(this, "Waiting for process access..."); - launcherAccess.get().waitValue(true).handle(seq::next); - }).then(seq -> { - Msg.debug(this, "Launching..."); - launcher.get().launch("notepad", "junk.txt").handle(seq::nextIgnore); - }).then(seq -> { - Msg.debug(this, "Waiting for session access (again)..."); - rootAccess.get().waitValue(true).handle(seq::next); - }).then(seq -> { - Msg.debug(this, "Getting Processes (after launch)..."); - model.fetchObjectElements(List.of("Sessions", "[0]", "Processes")) - .handle(seq::next); - }, t).then((elements, seq) -> { - Msg.debug(this, "Processes after: " + elements); - assertEquals(1, elements.size()); - seq.exit(); - }).finish()); - } - } - - @Test - public void testListAvailableProcesses() throws Throwable { - try (DummyProc np = runProc("notepad"); ModelHost m = modelHost()) { - DebuggerObjectModel model = m.getModel(); - - AtomicReference root = new AtomicReference<>(); - AtomicReference access = new AtomicReference<>(); - - waitOn(AsyncUtils.sequence(TypeSpec.VOID).then(seq -> { - m.init().handle(seq::next); - }).then(seq -> { - Msg.debug(this, "Getting session root object"); - model.fetchModelObject("Sessions", "[0]").handle(seq::next); - }, root).then(seq -> { - Msg.debug(this, "Tracking session access..."); - DebugModelConventions.trackAccessibility(root.get()).handle(seq::next); - }, access).then(seq -> { - Msg.debug(this, "Waiting for session access..."); - access.get().waitValue(true).handle(seq::next); - }).then(seq -> { - model.fetchObjectElements(List.of("Sessions", "[0]", "Available")) - .handle(seq::next); - }, DebuggerObjectModel.ELEMENT_MAP_TYPE).then((available, seq) -> { - assertTrue(available.containsKey(Long.toString(np.pid))); - seq.exit(); - }).finish()); - } - } - - @Test - public void testListProcesses() throws Throwable { - try (DummyProc np = runProc("notepad"); ModelHost m = modelHost()) { - DebuggerObjectModel model = m.getModel(); - - AtomicReference root = new AtomicReference<>(); - AtomicReference access = new AtomicReference<>(); - AtomicReference proc = new AtomicReference<>(); - - waitOn(AsyncUtils.sequence(TypeSpec.VOID).then(seq -> { - m.init().handle(seq::next); - }).then(seq -> { - Msg.debug(this, "Getting session root object"); - model.fetchModelObject("Sessions", "[0]").handle(seq::next); - }, root).then(seq -> { - Msg.debug(this, "Tracking session access..."); - DebugModelConventions.trackAccessibility(root.get()).handle(seq::next); - }, access).then(seq -> { - Msg.debug(this, "Waiting for session access..."); - access.get().waitValue(true).handle(seq::next); - }).then(seq -> { - Msg.debug(this, "Finding TargetLauncher..."); - DebugModelConventions.findSuitable(TargetAttacher.class, root.get()) - .handle( - seq::next); - }, proc).then(seq -> { - Msg.debug(this, "Attaching to bogus path..."); - TargetAttacher attacher = proc.get().as(TargetAttacher.class); - TODO(); - seq.next(null, null); - }).then(seq -> { - model.fetchObjectElements(List.of("Sessions", "[0]", "Processes")) - .handle(seq::next); - // NB: listProcesses will fail if no process is being debugged - }, DebuggerObjectModel.ELEMENT_MAP_TYPE).then((processes, seq) -> { - assertEquals(1, processes.size()); - seq.exit(); - }).finish()); - } - } - - @Test - public void testSessionAttachKill() throws Throwable { - try (DummyProc np = runProc("notepad"); ModelHost m = modelHost()) { - DebuggerObjectModel model = m.getModel(); - - AtomicReference root = new AtomicReference<>(); - AtomicReference access = new AtomicReference<>(); - AtomicReference attacher = new AtomicReference<>(); - AtomicReference attachable = new AtomicReference<>(); - TypeSpec> t = TypeSpec.auto(); - - waitOn(AsyncUtils.sequence(TypeSpec.VOID).then(seq -> { - m.init().handle(seq::next); - }).then(seq -> { - Msg.debug(this, "Getting session root object"); - model.fetchModelObject("Sessions", "[0]").handle(seq::next); - }, root).then(seq -> { - Msg.debug(this, "Tracking session access..."); - DebugModelConventions.trackAccessibility(root.get()).handle(seq::next); - }, access).then(seq -> { - Msg.debug(this, "Waiting for session access..."); - access.get().waitValue(true).handle(seq::next); - }).then(seq -> { -// Msg.debug(this, "Getting Processes (before attach)..."); -// model.getObjectElements(List.of("Sessions", "[0]", "Processes")).handle(seq::next); -// }, t).then((children, seq) -> { -// Msg.debug(this, "Processes before: " + children); -// assertEquals(1, children.size()); - - AsyncFence fence = new AsyncFence(); - Msg.debug(this, "Finding TargetAttacher..."); - fence.include(DebugModelConventions.findSuitable(TargetAttacher.class, root.get()) - .thenAccept(a -> { - Msg.debug(this, " Got TargetAttacher: " + a); - attacher.set(a); - })); - fence.include(model.fetchModelObject("Sessions", "[0]", "Available", - "[" + np.pid + "]") - .thenAccept(o -> { - Msg.debug(this, " Got Attachable: " + o); - assertTrue(o.getInterfaceNames().contains("Attachable")); - attachable.set((TargetAttachable) o); - })); - fence.ready().handle(seq::next); - }).then(seq -> { - Msg.debug(this, "Attaching..."); - attacher.get().attach(attachable.get()).handle(seq::nextIgnore); - }).then(seq -> { - Msg.debug(this, "Waiting for session access (again)..."); - access.get().waitValue(true).handle(seq::next); - }).then(seq -> { - Msg.debug(this, "Getting Processes (after attach)..."); - model.fetchObjectElements(List.of("Sessions", "[0]", "Processes")) - .handle(seq::next); - }, t).then((elements, seq) -> { - Msg.debug(this, "Processes after: " + elements); - assertEquals(1, elements.size()); - Msg.debug(this, "Killing..."); - TargetObject attached = elements.get("0"); - assertTrue(attached.getInterfaceNames().contains("Killable")); - TargetKillable killable = (TargetKillable) attached; - killable.kill().handle(seq::next); - }).finish()); - } - } - - @Test - public void testProcessAttachKill() throws Throwable { - try (DummyProc np = runProc("notepad"); ModelHost m = modelHost()) { - DebuggerObjectModel model = m.getModel(); - - AtomicReference root = new AtomicReference<>(); - AtomicReference access = new AtomicReference<>(); - AtomicReference obj = new AtomicReference<>(); - TypeSpec> t = TypeSpec.auto(); - - waitOn(AsyncUtils.sequence(TypeSpec.VOID).then(seq -> { - m.init().handle(seq::next); - }).then(seq -> { - Msg.debug(this, "Getting session root object"); - model.fetchModelObject("Sessions", "[0]").handle(seq::next); - }, root).then(seq -> { - Msg.debug(this, "Tracking session access..."); - DebugModelConventions.trackAccessibility(root.get()).handle(seq::next); - }, access).then(seq -> { - Msg.debug(this, "Waiting for session access..."); - access.get().waitValue(true).handle(seq::next); - }).then(seq -> { - Msg.debug(this, "Finding TargetLauncher..."); - DebugModelConventions.findSuitable(TargetAttacher.class, root.get()) - .handle( - seq::next); - }, obj).then(seq -> { - Msg.debug(this, "Attaching..."); - TargetAttacher attacher = obj.get().as(TargetAttacher.class); - attacher.attach(np.pid) - .handle(seq::nextIgnore); - }).then(seq -> { - Msg.debug(this, "Waiting for session access (again, again)..."); - access.get().waitValue(true).handle(seq::next); - }).then(seq -> { - Msg.debug(this, "Getting Processes (after attach)..."); - model.fetchObjectElements(List.of("Sessions", "[0]", "Processes")) - .handle(seq::next); - }, t).then((elements, seq) -> { - Msg.debug(this, "Processes after: " + elements); - assertEquals(1, elements.size()); - Msg.debug(this, "Killing..."); - TargetObject attached = elements.get("0"); - assertTrue(attached.getInterfaceNames().contains("Killable")); - TargetKillable killable = (TargetKillable) attached; - killable.kill().handle(seq::next); - }).finish()); - } - } - - @Test - public void testProcessAttachContKill() throws Throwable { - try (DummyProc np = runProc("notepad"); ModelHost m = modelHost()) { - DebuggerObjectModel model = m.getModel(); - - AtomicReference root = new AtomicReference<>(); - AtomicReference access = new AtomicReference<>(); - AtomicReference obj = new AtomicReference<>(); - TypeSpec> t = TypeSpec.auto(); - - waitOn(AsyncUtils.sequence(TypeSpec.VOID).then(seq -> { - m.init().handle(seq::next); - }).then(seq -> { - Msg.debug(this, "Getting session root object"); - model.fetchModelObject("Sessions", "[0]").handle(seq::next); - }, root).then(seq -> { - Msg.debug(this, "Tracking session access..."); - DebugModelConventions.trackAccessibility(root.get()).handle(seq::next); - }, access).then(seq -> { - Msg.debug(this, "Waiting for session access..."); - access.get().waitValue(true).handle(seq::next); - }).then(seq -> { - Msg.debug(this, "Finding TargetLauncher..."); - DebugModelConventions.findSuitable(TargetAttacher.class, root.get()) - .handle( - seq::next); - }, obj).then(seq -> { - Msg.debug(this, "Attaching..."); - TargetAttacher attacher = obj.get().as(TargetAttacher.class); - attacher.attach(np.pid) - .handle(seq::nextIgnore); - }).then(seq -> { - Msg.debug(this, "Waiting for session access (again, again)..."); - access.get().waitValue(true).handle(seq::next); - }).then(seq -> { - Msg.debug(this, "Getting Process 1..."); - model.fetchModelObject("Sessions", "[0]", "Processes", "[0]").handle(seq::next); - }, obj).then(seq -> { - Msg.debug(this, "Resuming..."); - TargetResumable resumable = obj.get().as(TargetResumable.class); - resumable.resume().handle(seq::next); - }).then(seq -> { - Msg.debug(this, "Waiting for session access (after resume)..."); - access.get().waitValue(true).handle(seq::next); - }).then(seq -> { - Msg.debug(this, "Getting Processes (after attach)..."); - model.fetchObjectElements(List.of("Sessions", "[0]", "Processes")) - .handle(seq::next); - }, t).then((elements, seq) -> { - Msg.debug(this, "Processes after: " + elements); - assertEquals(1, elements.size()); - Msg.debug(this, "Killing..."); - TargetObject attached = elements.get("0"); - assertTrue(attached.getInterfaceNames().contains("Killable")); - TargetKillable killable = (TargetKillable) attached; - killable.kill().handle(seq::nextIgnore); - }).finish()); - } - } - - @Test - public void testLaunchContExit() throws Throwable { - try (ModelHost m = modelHost()) { - DebuggerObjectModel model = m.getModel(); - - AtomicReference root = new AtomicReference<>(); - AtomicReference access = new AtomicReference<>(); - AtomicReference obj = new AtomicReference<>(); - waitOn(AsyncUtils.sequence(TypeSpec.VOID).then(seq -> { - m.init().handle(seq::next); - }).then(seq -> { - Msg.debug(this, "Getting session root object"); - model.fetchModelObject("Sessions", "[0]").handle(seq::next); - }, root).then(seq -> { - Msg.debug(this, "Tracking session access..."); - DebugModelConventions.trackAccessibility(root.get()).handle(seq::next); - }, access).then(seq -> { - Msg.debug(this, "Waiting for session access..."); - access.get().waitValue(true).handle(seq::next); - }).then(seq -> { - Msg.debug(this, "Finding TargetLauncher..."); - DebugModelConventions.findSuitable(DbgModelTargetLauncher.class, root.get()) - .handle(seq::next); - }, obj).then(seq -> { - Msg.debug(this, "Launching..."); - TargetLauncher launcher = obj.get().as(TargetLauncher.class); - launcher.launch(Map.of(TargetCmdLineLauncher.CMDLINE_ARGS_NAME, "notepad junk.txt")) - .handle(seq::nextIgnore); - }).then(seq -> { - Msg.debug(this, "Waiting for session access (again)..."); - access.get().waitValue(true).handle(seq::next); - }).then(seq -> { - Msg.debug(this, "Getting Process 1..."); - model.fetchModelObject("Sessions", "[0]", "Processes", "[0]").handle(seq::next); - }, obj).then(seq -> { - Msg.debug(this, "Resuming..."); - TargetResumable resumable = obj.get().as(TargetResumable.class); - resumable.resume().handle(seq::next); - }).then(seq -> { - Msg.debug(this, "Waiting for session access (after resume)..."); - access.get().waitValue(true).handle(seq::next); - }).finish()); - } - } - - @Test(expected = DebuggerModelNoSuchPathException.class) - public void testAttachNoObjectErr() throws Throwable { - try (ModelHost m = modelHost()) { - DebuggerObjectModel model = m.getModel(); - - AtomicReference root = new AtomicReference<>(); - AtomicReference access = new AtomicReference<>(); - AtomicReference proc = new AtomicReference<>(); - - waitOn(AsyncUtils.sequence(TypeSpec.VOID).then(seq -> { - m.init().handle(seq::next); - }).then(seq -> { - Msg.debug(this, "Getting session root object"); - model.fetchModelObject("Sessions", "[0]").handle(seq::next); - }, root).then(seq -> { - Msg.debug(this, "Tracking session access..."); - DebugModelConventions.trackAccessibility(root.get()).handle(seq::next); - }, access).then(seq -> { - Msg.debug(this, "Waiting for session access..."); - access.get().waitValue(true).handle(seq::next); - }).then(seq -> { - Msg.debug(this, "Finding TargetLauncher..."); - DebugModelConventions.findSuitable(TargetAttacher.class, root.get()) - .handle( - seq::next); - }, proc).then(seq -> { - Msg.debug(this, "Attaching to bogus path..."); - TargetAttacher attacher = proc.get().as(TargetAttacher.class); - TODO(); - seq.next(null, null); - }).finish()); - } - } - - @Test(expected = DebuggerModelTypeException.class) - public void testAttachNonAttachableErr() throws Throwable { - try (ModelHost m = modelHost()) { - DebuggerObjectModel model = m.getModel(); - - AtomicReference root = new AtomicReference<>(); - AtomicReference access = new AtomicReference<>(); - AtomicReference proc = new AtomicReference<>(); - - waitOn(AsyncUtils.sequence(TypeSpec.VOID).then(seq -> { - m.init().handle(seq::next); - }).then(seq -> { - Msg.debug(this, "Getting session root object"); - model.fetchModelObject("Sessions", "[0]").handle(seq::next); - }, root).then(seq -> { - Msg.debug(this, "Tracking session access..."); - DebugModelConventions.trackAccessibility(root.get()).handle(seq::next); - }, access).then(seq -> { - Msg.debug(this, "Waiting for session access..."); - access.get().waitValue(true).handle(seq::next); - }).then(seq -> { - Msg.debug(this, "Finding TargetLauncher..."); - DebugModelConventions.findSuitable(TargetAttacher.class, root.get()) - .handle( - seq::next); - }, proc).then(seq -> { - Msg.debug(this, "Attaching to bogus path..."); - TargetAttacher attacher = proc.get().as(TargetAttacher.class); - TODO(); - seq.next(null, null); - }).finish()); - fail("Exception expected"); - } - } - - @Test - @Ignore("for developer workstation") - public void stressTestExecute() throws Throwable { - for (int i = 0; i < 100; i++) { - testExecute(); - } - } - - //@Test - public void testExecute() throws Throwable { - try (ModelHost m = modelHost()) { - DebuggerObjectModel model = m.getModel(); - - AtomicReference root = new AtomicReference<>(); - AtomicReference access = new AtomicReference<>(); - AsyncReference lastOut = new AsyncReference<>(); - - DebuggerModelListener l = new DebuggerModelListener() { - @Override - public void consoleOutput(TargetObject interpreter, Channel channel, - byte[] out) { - String str = new String(out); - Msg.debug(this, "Got " + channel + " output: " + str); - lastOut.set(str, null); - } - }; - - waitOn(AsyncUtils.sequence(TypeSpec.VOID).then(seq -> { - m.init().handle(seq::next); - }).then(seq -> { - Msg.debug(this, "Getting session root object..."); - model.fetchModelObject("Sessions", "[0]").handle(seq::next); - }, root).then(seq -> { - root.get().addListener(l); - Msg.debug(this, "Tracking session access..."); - DebugModelConventions.trackAccessibility(root.get()).handle(seq::next); - }, access).then(seq -> { - Msg.debug(this, "Waiting for session access..."); - access.get().waitValue(true).handle(seq::next); - }).then(seq -> { - Msg.debug(this, "Running command..."); - TargetInterpreter interpreter = root.get().as(TargetInterpreter.class); - interpreter.execute(".echo xyzzy").handle(seq::next); - }).then(seq -> { - Msg.debug(this, "Waiting for expected output..."); - lastOut.waitValue("xyzzy\n").handle(seq::next); - }).finish()); - } - } - - @Test - public void testExecuteCapture() throws Throwable { - try (ModelHost m = modelHost(); CatchOffThread offThread = new CatchOffThread()) { - DebuggerObjectModel model = m.getModel(); - - AtomicReference root = new AtomicReference<>(); - AtomicReference access = new AtomicReference<>(); - - DebuggerModelListener l = new DebuggerModelListener() { - @Override - public void consoleOutput(TargetObject interpreter, Channel channel, - String out) { - String str = new String(out); - Msg.debug(this, "Got " + channel + " output: " + str); - if (!str.contains("test")) { - return; - } - offThread.catching(() -> fail("Unexpected output:" + str)); - } - }; - - waitOn(AsyncUtils.sequence(TypeSpec.VOID).then(seq -> { - m.init().handle(seq::next); - }).then(seq -> { - Msg.debug(this, "Getting session root object..."); - model.fetchModelObject("Sessions", "[0]").handle(seq::next); - }, root).then(seq -> { - root.get().addListener(l); - Msg.debug(this, "Tracking session access..."); - DebugModelConventions.trackAccessibility(root.get()).handle(seq::next); - }, access).then(seq -> { - Msg.debug(this, "Waiting for session access..."); - access.get().waitValue(true).handle(seq::next); - }).then(seq -> { - Msg.debug(this, "Running command with capture..."); - TargetInterpreter interpreter = root.get().as(TargetInterpreter.class); - interpreter.executeCapture(".echo xyzzy").handle(seq::next); - }, TypeSpec.STRING).then((out, seq) -> { - Msg.debug(this, "Captured: " + out); - assertTrue(out.contains("xyzzy")); - seq.exit(); - }).finish()); - } - } - - @Test - public void testGetBreakKinds() throws Throwable { - try (ModelHost m = modelHost()) { - DebuggerObjectModel model = m.getModel(); - - AtomicReference root = new AtomicReference<>(); - AtomicReference access = new AtomicReference<>(); - AtomicReference breaks = new AtomicReference<>(); - - waitOn(AsyncUtils.sequence(TypeSpec.VOID).then(seq -> { - m.init().handle(seq::next); - }).then(seq -> { - Msg.debug(this, "Getting session root object..."); - model.fetchModelObject("Sessions", "[0]").handle(seq::next); - }, root).then(seq -> { - Msg.debug(this, "Tracking session access..."); - DebugModelConventions.trackAccessibility(root.get()).handle(seq::next); - }, access).then(seq -> { - Msg.debug(this, "Waiting for session access..."); - access.get().waitValue(true).handle(seq::next); - }).then(seq -> { - Msg.debug(this, "Finding breakpoint container..."); - DebugModelConventions.findSuitable(TargetBreakpointSpecContainer.class, root.get()) - .handle(seq::next); - }, breaks).then(seq -> { - Msg.debug(this, "Got: " + breaks); - TargetBreakpointKindSet kinds = breaks.get().getSupportedBreakpointKinds(); - Msg.debug(this, "Supports: " + kinds); - assertEquals(4, kinds.size()); - seq.exit(); - }).finish()); - } - } - - public static final TypeSpec> BL_COL_SPEC = - null; - - @Test - public void testPlaceBreakpoint() throws Throwable { - try (ModelHost m = modelHost()) { - DebuggerObjectModel model = m.getModel(); - - AtomicReference root = new AtomicReference<>(); - AtomicReference access = new AtomicReference<>(); - AtomicReference launcher = new AtomicReference<>(); - AtomicReference breaks = new AtomicReference<>(); - AtomicReference loc = new AtomicReference<>(); - - waitOn(AsyncUtils.sequence(TypeSpec.VOID).then(seq -> { - m.init().handle(seq::next); - }).then(seq -> { - Msg.debug(this, "Getting session root object..."); - model.fetchModelObject("Sessions", "[0]").handle(seq::next); - }, root).then(seq -> { - Msg.debug(this, "Tracking session access..."); - DebugModelConventions.trackAccessibility(root.get()).handle(seq::next); - }, access).then(seq -> { - Msg.debug(this, "Waiting for session access..."); - access.get().waitValue(true).handle(seq::next); - }).then(seq -> { - Msg.debug(this, "Finding TargetLauncher..."); - DebugModelConventions.findSuitable(DbgModelTargetLauncher.class, root.get()) - .handle( - seq::next); - }, launcher).then(seq -> { - Msg.debug(this, "Launching..."); - launcher.get() - .launch(Map.of(TargetCmdLineLauncher.CMDLINE_ARGS_NAME, "notepad junk.txt")) - .handle(seq::nextIgnore); - }).then(seq -> { - Msg.debug(this, "Finding breakpoint container..."); - DebugModelConventions.findSuitable(TargetBreakpointSpecContainer.class, root.get()) - .handle(seq::next); - }, breaks).then(seq -> { - Msg.debug(this, "Placing breakpoint..."); - breaks.get() - .placeBreakpoint("0x7ff7d52c8987", - Set.of(TargetBreakpointKind.SW_EXECUTE)) - .handle(seq::next); - }).then(seq -> { - Msg.debug(this, "Getting breakpoint specs..."); - breaks.get() - .fetchElements() - .handle(seq::next); - }, DebuggerObjectModel.ELEMENT_MAP_TYPE).then((specs, seq) -> { - Msg.debug(this, "Got specs: " + specs); - assertEquals(1, specs.size()); - TargetBreakpointSpec spec = specs.get("0").as(TargetBreakpointSpec.class); - spec.getLocations().handle(seq::next); - }, BL_COL_SPEC).then((es, seq) -> { - Msg.debug(this, "Got effectives: " + es); - assertEquals(1, es.size()); - loc.set(es.iterator().next()); - Address addr = loc.get().getAddress(); - Msg.debug(this, "Got address: " + addr); - seq.exit(); - }).finish()); - } - } - - @Test - public void testStack() throws Throwable { - try (ModelHost m = modelHost()) { - DebuggerObjectModel model = m.getModel(); - - AtomicReference root = new AtomicReference<>(); - AtomicReference access = new AtomicReference<>(); - AtomicReference breaks = new AtomicReference<>(); - AtomicReference launcher = new AtomicReference<>(); - AtomicReference obj = new AtomicReference<>(); - waitOn(AsyncUtils.sequence(TypeSpec.VOID).then(seq -> { - m.init().handle(seq::next); - }).then(seq -> { - Msg.debug(this, "Getting session root object"); - model.fetchModelObject("Sessions", "[0]").handle(seq::next); - }, root).then(seq -> { - Msg.debug(this, "Tracking session access..."); - DebugModelConventions.trackAccessibility(root.get()).handle(seq::next); - }, access).then(seq -> { - Msg.debug(this, "Waiting for session access..."); - access.get().waitValue(true).handle(seq::next); - }).then(seq -> { - Msg.debug(this, "Finding TargetLauncher..."); - DebugModelConventions.findSuitable(TargetLauncher.class, root.get()) - .handle( - seq::next); - }, launcher).then(seq -> { - Msg.debug(this, "Launching..."); - launcher.get() - .launch(Map.of("args", List.of("notepad", "junk.txt"))) - .handle(seq::nextIgnore); - }).then(seq -> { - Msg.debug(this, "Waiting for session access (again)..."); - access.get().waitValue(true).handle(seq::next); - }).then(seq -> { - Msg.debug(this, "Finding breakpoint container..."); - DebugModelConventions.findSuitable(TargetBreakpointSpecContainer.class, root.get()) - .handle(seq::next); - }, breaks).then(seq -> { - Msg.debug(this, "Placing breakpoint..."); - breaks.get() - .placeBreakpoint("0x7ff7d52c8987", - Set.of(TargetBreakpointKind.SW_EXECUTE)) - .handle(seq::next); - }).then(seq -> { - Msg.debug(this, "Getting Process 1..."); - model.fetchModelObject("Sessions", "[0]", "Processes", "[0]").handle(seq::next); - }, obj).then(seq -> { - Msg.debug(this, "Resuming..."); - TargetResumable resumable = obj.get().as(TargetResumable.class); - resumable.resume().handle(seq::next); - }).then(seq -> { - Msg.debug(this, "Waiting for session access (after resume)..."); - access.get().waitValue(true).handle(seq::next); - }).then(seq -> { - obj.get().fetchSubElements("Threads", "[0]", "Stack").handle(seq::next); - }, DebuggerObjectModel.ELEMENT_MAP_TYPE).then((frames, seq) -> { - Msg.debug(this, "Got stack:"); - for (Map.Entry ent : frames.entrySet()) { - TargetStackFrame frame = ent.getValue().as(TargetStackFrame.class); - Msg.debug(this, ent.getKey() + ": " + frame.getProgramCounter()); - } - long offset = frames.get("0") - .getTypedAttributeNowByName("pc", Address.class, - null) - .getOffset(); - assertEquals(0x7fffadd6e890L, offset); - //assertEquals("main", frames.get("" + (frames.size() - 1)) - // .getTypedAttributeNowByName("function", String.class, null)); - seq.exit(); - }).finish()); - } - } - - @Test - public void testRegisters() throws Throwable { - try (ModelHost m = modelHost()) { - DebuggerObjectModel model = m.getModel(); - - AtomicReference root = new AtomicReference<>(); - AtomicReference access = new AtomicReference<>(); - AtomicReference launcher = new AtomicReference<>(); - AtomicReference proc = new AtomicReference<>(); - AtomicReference bank = new AtomicReference<>(); - Set descs = new LinkedHashSet<>(); - - waitOn(AsyncUtils.sequence(TypeSpec.VOID).then(seq -> { - m.init().handle(seq::next); - }).then(seq -> { - Msg.debug(this, "Getting session root object"); - model.fetchModelObject("Sessions", "[0]").handle(seq::next); - }, root).then(seq -> { - Msg.debug(this, "Tracking session access..."); - DebugModelConventions.trackAccessibility(root.get()).handle(seq::next); - }, access).then(seq -> { - Msg.debug(this, "Waiting for session access..."); - access.get().waitValue(true).handle(seq::next); - }).then(seq -> { - Msg.debug(this, "Finding TargetLauncher..."); - DebugModelConventions.findSuitable(DbgModelTargetLauncher.class, root.get()) - .handle( - seq::next); - }, launcher).then(seq -> { - Msg.debug(this, "Launching..."); - launcher.get() - .launch(Map.of("args", List.of("notepad", "junk.txt"))) - .handle(seq::nextIgnore); - }).then(seq -> { - Msg.debug(this, "Waiting for session access (again)..."); - access.get().waitValue(true).handle(seq::next); - }).then(seq -> { - Msg.debug(this, "Getting Process 1..."); - model.fetchModelObject(List.of("Sessions", "[0]", "Processes", "[0]")) - .handle( - seq::next); - }, proc).then(seq -> { - proc.get().fetchSuccessor("Threads", "[0]", "Stack", "[0]").thenAccept(top -> { - bank.set(top.as(TargetRegisterBank.class)); - }).handle(seq::next); - }).then(seq -> { - Msg.debug(this, "Got bank: " + bank.get()); - Msg.debug(this, "Descriptions ref: " + bank.get().getDescriptions()); - TargetRegisterContainer cont = bank.get().getDescriptions(); - Msg.debug(this, "Register descriptions: " + cont); - cont.getRegisters().thenAccept(descs::addAll).handle(seq::next); - }).then(seq -> { - Msg.debug(this, "Elements: "); - for (TargetRegister reg : descs) { - Msg.debug(this, " " + reg.getIndex() + ": " + reg.getBitLength()); - } - bank.get().readRegisters(descs).handle(seq::next); - }, TypeSpec.map(String.class, byte[].class)).then((data, seq) -> { - Msg.debug(this, "Values: "); - for (Map.Entry ent : data.entrySet()) { - Msg.debug(this, " " + ent.getKey() + " = " + - NumericUtilities.convertBytesToString(ent.getValue())); - } - // TODO: Implement Environment, and port these tests - Msg.debug(this, "Writing two registers, general and vector"); - bank.get().writeRegistersNamed(AMD64_TEST_REG_VALUES).handle(seq::next); - }).then(seq -> { - Msg.debug(this, "Flushing cache"); - bank.get().invalidateCaches().handle(seq::next); - }).then(seq -> { - Msg.debug(this, "Re-reading values"); - // NOTE: Can't reliably step here since rax may be referenced by the instruction - bank.get().readRegistersNamed(AMD64_TEST_REG_VALUES.keySet()).handle(seq::next); - }, TypeSpec.map(String.class, byte[].class)).then((data, seq) -> { - assertEquals(hexlify(AMD64_TEST_REG_VALUES), hexlify(data)); - seq.exit(); - }).finish()); - } - } - - @Test - public void testFocusProcesses() throws Throwable { - try (DummyProc np = runProc("notepad"); ModelHost m = modelHost()) { - DebuggerObjectModel model = m.getModel(); - - AtomicReference root = new AtomicReference<>(); - AtomicReference scope = new AtomicReference<>(); - AtomicReference access = new AtomicReference<>(); - AtomicReference processes = new AtomicReference<>(); - AtomicReference obj1 = new AtomicReference<>(); - AtomicReference obj2 = new AtomicReference<>(); - AsyncReference, Void> focusProcPath = new AsyncReference<>(); - AsyncReference processCount = new AsyncReference<>(); - - DebuggerModelListener procListener = new DebuggerModelListener() { - @Override - public void elementsChanged(TargetObject parent, Collection removed, - Map added) { - processCount.set(processes.get().getCachedElements().size(), null); - } - }; - DebuggerModelListener focusListener = - new AnnotatedDebuggerAttributeListener(MethodHandles.lookup()) { - @AttributeCallback(TargetFocusScope.FOCUS_ATTRIBUTE_NAME) - public void focusChanged(TargetObject object, TargetObject focused) { - // Truncate the path to the parent process - focusProcPath.set(focused.getPath().subList(0, 2), null); - } - }; - - waitOn(AsyncUtils.sequence(TypeSpec.VOID).then(seq -> { - m.init().handle(seq::next); - }).then(seq -> { - Msg.debug(this, "Getting session root object"); - model.fetchModelObject("Sessions", "[0]").handle(seq::next); - }, root).then(seq -> { - scope.set(root.get().as(TargetFocusScope.class)); - scope.get().addListener(focusListener); - Msg.debug(this, "Tracking session access..."); - DebugModelConventions.trackAccessibility(root.get()).handle(seq::next); - }, access).then(seq -> { - Msg.debug(this, "Waiting for session access..."); - access.get().waitValue(true).handle(seq::next); - }).then(seq -> { - Msg.debug(this, "Finding TargetLauncher..."); - DebugModelConventions.findSuitable(DbgModelTargetLauncher.class, root.get()) - .handle( - seq::next); - }, obj1).then(seq -> { - Msg.debug(this, "Attaching..."); - TargetAttacher attacher = obj1.get().as(TargetAttacher.class); - attacher.attach(np.pid).handle(seq::nextIgnore); - }).then(seq -> { - Msg.debug(this, "Getting processes container"); - model.fetchModelObject("Sessions", "[0]", "Processes").handle(seq::next); - }, processes).then(seq -> { - processes.get().addListener(procListener); - Msg.debug(this, "Finding TargetLauncher..."); - DebugModelConventions.findSuitable(DbgModelTargetLauncher.class, root.get()) - .handle( - seq::next); - }, obj2).then(seq -> { - Msg.debug(this, "Creating another process"); - TargetLauncher launcher = obj2.get().as(TargetLauncher.class); - launcher.launch(Map.of(TargetCmdLineLauncher.CMDLINE_ARGS_NAME, "notepad junk.txt")) - .handle(seq::nextIgnore); - }).then(seq -> { - Msg.debug(this, "Waiting for session access (again)..."); - access.get().waitValue(true).handle(seq::next); - }).then(seq -> { - assertTrue(PathUtils.isAncestor(List.of("Sessions", "[0]", "Processes", "[1]"), - scope.get().getFocus().getPath())); - // Redundant, but verifies the listener is keeping up - assertEquals(List.of("Sessions", "[0]", "Processes", "[1]"), focusProcPath.get()); - - Msg.debug(this, "Requesting focus on process 0"); - AsyncFence fence = new AsyncFence(); - TargetObject p2 = model.getModelObject("Sessions", "[0]", "Processes", "[0]"); - fence.include(focusProcPath.waitValue(p2.getPath())); - fence.include(scope.get().requestFocus(p2)); - fence.ready().handle(seq::next); - }).then(seq -> { - assertTrue(PathUtils.isAncestor(List.of("Sessions", "[0]", "Processes", "[0]"), - scope.get().getFocus().getPath())); - // Redundant, but verifies the listener is keeping up - assertEquals(List.of("Sessions", "[0]", "Processes", "[0]"), focusProcPath.get()); - seq.exit(); - }).finish()); - } - } - - protected void init(ModelHost m) throws Throwable { - waitOn(m.init()); - } - - @Test - public void testSerializeSchema() throws Throwable { - try (ModelHost m = modelHost()) { - DebuggerObjectModel model = m.getModel(); - init(m); - - TargetObjectSchema rootSchema = model.getRootSchema(); - String serialized = XmlSchemaContext.serialize(rootSchema.getContext()); - System.out.println(serialized); - - assertEquals("Debugger", rootSchema.getName().toString()); - } - } -} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/garbage/GadpForDbgTest.java b/Ghidra/Debug/Debugger-agent-dbgeng/garbage/GadpForDbgTest.java deleted file mode 100644 index 76326a626d..0000000000 --- a/Ghidra/Debug/Debugger-agent-dbgeng/garbage/GadpForDbgTest.java +++ /dev/null @@ -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 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()); - } - } -} diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/garbage/ModelForDbgTest.java b/Ghidra/Debug/Debugger-agent-dbgeng/garbage/ModelForDbgTest.java deleted file mode 100644 index 6aaf905bba..0000000000 --- a/Ghidra/Debug/Debugger-agent-dbgeng/garbage/ModelForDbgTest.java +++ /dev/null @@ -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 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(); - } -} diff --git a/Ghidra/Debug/Debugger-agent-frida/FridaNotes.txt b/Ghidra/Debug/Debugger-agent-frida/FridaNotes.txt new file mode 100644 index 0000000000..6d759a4364 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-frida/FridaNotes.txt @@ -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. diff --git a/Ghidra/Debug/Debugger-agent-frida/Module.manifest b/Ghidra/Debug/Debugger-agent-frida/Module.manifest new file mode 100644 index 0000000000..e69de29bb2 diff --git a/Ghidra/Debug/Debugger-agent-frida/build.gradle b/Ghidra/Debug/Debugger-agent-frida/build.gradle new file mode 100644 index 0000000000..d4a95dca62 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-frida/build.gradle @@ -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") + } +} diff --git a/Ghidra/Debug/Debugger-agent-frida/certification.manifest b/Ghidra/Debug/Debugger-agent-frida/certification.manifest new file mode 100644 index 0000000000..b9c2c0cdad --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-frida/certification.manifest @@ -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| diff --git a/Ghidra/Debug/Debugger-agent-frida/data/scripts/onAccess.js b/Ghidra/Debug/Debugger-agent-frida/data/scripts/onAccess.js new file mode 100644 index 0000000000..f5c36eb3b1 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-frida/data/scripts/onAccess.js @@ -0,0 +1,5 @@ +onAccess: function(details) { + console.log(details.from + " " + details.operation + " " + details.address); + console.log("pages: completed=" + details.pagesCompleted + " total=" + details.pageTotal); +} + diff --git a/Ghidra/Debug/Debugger-agent-frida/data/scripts/onAccessExt.js b/Ghidra/Debug/Debugger-agent-frida/data/scripts/onAccessExt.js new file mode 100644 index 0000000000..00604973b5 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-frida/data/scripts/onAccessExt.js @@ -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); + }); + } + }); +} + diff --git a/Ghidra/Debug/Debugger-agent-frida/data/scripts/onCallSummary.js b/Ghidra/Debug/Debugger-agent-frida/data/scripts/onCallSummary.js new file mode 100644 index 0000000000..3d0a0ba4d7 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-frida/data/scripts/onCallSummary.js @@ -0,0 +1 @@ +onCallSummary(summary) { console.log(JSON.stringify(summary)); } diff --git a/Ghidra/Debug/Debugger-agent-frida/data/scripts/onEnter.js b/Ghidra/Debug/Debugger-agent-frida/data/scripts/onEnter.js new file mode 100644 index 0000000000..fc6bc94ae3 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-frida/data/scripts/onEnter.js @@ -0,0 +1,2 @@ +onEnter: function(args) {console.log("entering");} + diff --git a/Ghidra/Debug/Debugger-agent-frida/data/scripts/onLeave.js b/Ghidra/Debug/Debugger-agent-frida/data/scripts/onLeave.js new file mode 100644 index 0000000000..1e2d8790cf --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-frida/data/scripts/onLeave.js @@ -0,0 +1,2 @@ +onEnter: function(args) {console.log("intercept=>leaving");} + diff --git a/Ghidra/Debug/Debugger-agent-frida/data/scripts/onReceive.js b/Ghidra/Debug/Debugger-agent-frida/data/scripts/onReceive.js new file mode 100644 index 0000000000..51b47bbb3e --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-frida/data/scripts/onReceive.js @@ -0,0 +1 @@ +onReceive(events) { console.log(JSON.stringify(events)); } diff --git a/Ghidra/Debug/Debugger-agent-frida/ghidra_scripts/FridaTestScript.java b/Ghidra/Debug/Debugger-agent-frida/ghidra_scripts/FridaTestScript.java new file mode 100644 index 0000000000..fd184ec4f4 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-frida/ghidra_scripts/FridaTestScript.java @@ -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]); + } +} diff --git a/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/FridaInJvmDebuggerModelFactory.java b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/FridaInJvmDebuggerModelFactory.java new file mode 100644 index 0000000000..fa7017795b --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/FridaInJvmDebuggerModelFactory.java @@ -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 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"); + } + +} diff --git a/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/frida/FridaClient.java b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/frida/FridaClient.java new file mode 100644 index 0000000000..1015b79bf5 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/frida/FridaClient.java @@ -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 args, List envp, String workingDir); + + FridaSession createProcess(FridaServerId si, String fileName, List args, List envp, + List pathsIO, + String workingDir, long createFlags, boolean stopAtEntry); + + void terminateCurrentProcess(FridaTarget target); + + void destroyCurrentProcess(FridaTarget target); + + void detachCurrentProcess(FridaTarget target); + + FridaTarget connectSession(String commandLine); + + Map listSessions(); + + void endSession(FridaTarget target, DebugEndSessionFlags flags); + + DebugStatus getExecutionStatus(); + + void processEvent(FridaEvent fridaEvt); + + boolean getInterrupt(); + + public void setManager(FridaManager manager); + +} diff --git a/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/frida/FridaClientImpl.java b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/frida/FridaClientImpl.java new file mode 100644 index 0000000000..11aedd0a8c --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/frida/FridaClientImpl.java @@ -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 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(), new ArrayList(), ""); + } + + @Override + public FridaSession createProcess(FridaServerId si, String fileName, + List args, List 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 args, List envp, List 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 listSessions() { + Map map = new HashMap<>(); + //List 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); + } + +} diff --git a/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/frida/FridaClientReentrant.java b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/frida/FridaClientReentrant.java new file mode 100644 index 0000000000..5da8f83bab --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/frida/FridaClientReentrant.java @@ -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(); + +} diff --git a/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/frida/FridaEng.java b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/frida/FridaEng.java new file mode 100644 index 0000000000..8705663b5d --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/frida/FridaEng.java @@ -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 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 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 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 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 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 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); + } + } + + + +} diff --git a/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/frida/FridaModuleInfo.java b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/frida/FridaModuleInfo.java new file mode 100644 index 0000000000..0a6c6cb60a --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/frida/FridaModuleInfo.java @@ -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 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; + } +} diff --git a/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/frida/FridaProcessInfo.java b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/frida/FridaProcessInfo.java new file mode 100644 index 0000000000..642e99f459 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/frida/FridaProcessInfo.java @@ -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; + } + +} diff --git a/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/frida/FridaRegionInfo.java b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/frida/FridaRegionInfo.java new file mode 100644 index 0000000000..94e52184e2 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/frida/FridaRegionInfo.java @@ -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 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; + } +} diff --git a/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/frida/FridaServerId.java b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/frida/FridaServerId.java new file mode 100644 index 0000000000..36ed6d874e --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/frida/FridaServerId.java @@ -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 { + 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 ""; + } +} diff --git a/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/frida/FridaSessionInfo.java b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/frida/FridaSessionInfo.java new file mode 100644 index 0000000000..fbf94f6c8d --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/frida/FridaSessionInfo.java @@ -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; + } +} diff --git a/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/frida/FridaThreadInfo.java b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/frida/FridaThreadInfo.java new file mode 100644 index 0000000000..5f1d6c78d4 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/frida/FridaThreadInfo.java @@ -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; + } +} diff --git a/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/gadp/FridaGadpServer.java b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/gadp/FridaGadpServer.java new file mode 100644 index 0000000000..b6d9524835 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/gadp/FridaGadpServer.java @@ -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 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 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 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(); + } +} diff --git a/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/gadp/FridaLocalDebuggerModelFactory.java b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/gadp/FridaLocalDebuggerModelFactory.java new file mode 100644 index 0000000000..ba16d00595 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/gadp/FridaLocalDebuggerModelFactory.java @@ -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 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 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 cmd) { + cmd.add(getServerClass().getCanonicalName()); + cmd.addAll(List.of("-H", host)); + cmd.addAll(List.of("-p", Integer.toString(port))); + } +} diff --git a/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/gadp/impl/AbstractClientThreadExecutor.java b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/gadp/impl/AbstractClientThreadExecutor.java new file mode 100644 index 0000000000..3e26de5709 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/gadp/impl/AbstractClientThreadExecutor.java @@ -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. + * + *

+ * 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 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 { + 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 shutdownNow() { + shuttingDown = true; + thread.interrupt(); + List 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 command) { + execute(priority, () -> command.accept(client)); + } + + public abstract FridaManager getManager(); + + public abstract void setManager(FridaManager manager); +} diff --git a/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/gadp/impl/FridaClientThreadExecutor.java b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/gadp/impl/FridaClientThreadExecutor.java new file mode 100644 index 0000000000..e637c051f1 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/gadp/impl/FridaClientThreadExecutor.java @@ -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 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 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; + } + +} diff --git a/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/gadp/impl/FridaGadpServerImpl.java b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/gadp/impl/FridaGadpServerImpl.java new file mode 100644 index 0000000000..365eed3273 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/gadp/impl/FridaGadpServerImpl.java @@ -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 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(); + } +} diff --git a/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/jna/FridaNative.java b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/jna/FridaNative.java new file mode 100644 index 0000000000..c9cecb5d4b --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/jna/FridaNative.java @@ -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 FIELDS = createFieldsOrder("domain", "code", "message"); + + public volatile int domain; + public volatile int code; + public volatile String message; + + @Override + protected List getFieldOrder() { + return FIELDS; + } + } + + public static class GumMemoryRange extends Structure { + public static class ByReference extends GumMemoryRange + implements Structure.ByReference { + // NO CODE + } + + public static final List FIELDS = createFieldsOrder("address", "size"); + + public NativeLong address; + public NativeLong size; + + @Override + protected List getFieldOrder() { + return FIELDS; + } + } + + public static class GumFileMapping extends Structure { + public static class ByReference extends GumFileMapping + implements Structure.ByReference { + // NO CODE + } + + public static final List FIELDS = createFieldsOrder("path", "offset", "size"); + + public Pointer path; + public NativeLong offset; + public NativeLong size; + + @Override + protected List getFieldOrder() { + return FIELDS; + } + } + + public static class GumModuleDetails extends Structure { + public static class ByReference extends GumModuleDetails + implements Structure.ByReference { + // NO CODE + } + + public static final List FIELDS = createFieldsOrder("name", "range", "path"); + + public Pointer name; + public GumMemoryRange.ByReference range; + public Pointer path; + + @Override + protected List getFieldOrder() { + return FIELDS; + } + } + + public static class GumRangeDetails extends Structure { + public static class ByReference extends GumRangeDetails + implements Structure.ByReference { + // NO CODE + } + + public static final List FIELDS = createFieldsOrder("range", "protection", "file"); + + public GumMemoryRange.ByReference range; + public NativeLong protection; + public GumFileMapping.ByReference file; + + @Override + protected List getFieldOrder() { + return FIELDS; + } + } + + public static class GumReturnAddressDetails extends Structure { + + public static class ByReference extends GumReturnAddressDetails + implements Structure.ByReference { + // NO CODE + } + + public static final List 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 getFieldOrder() { + return FIELDS; + } + } + + public static class GumReturnAddressArray extends Structure { + public static class ByReference extends GumReturnAddressArray + implements Structure.ByReference { + // NO CODE + } + + public static final List FIELDS = createFieldsOrder("len", "items"); + + public NativeLong len; + public NativeLong[] items = new NativeLong[GUM_MAX_BACKTRACE_DEPTH]; + + @Override + protected List getFieldOrder() { + return FIELDS; + } + } + + public static class GumMallocRangeDetails extends Structure { + public static class ByReference extends GumMallocRangeDetails + implements Structure.ByReference { + // NO CODE + } + + public static final List FIELDS = createFieldsOrder("range"); + + public GumMemoryRange.ByReference range; + + @Override + protected List 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); + +} diff --git a/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/FridaApplication.java b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/FridaApplication.java new file mode 100644 index 0000000000..23af4faa14 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/FridaApplication.java @@ -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; + } + +} diff --git a/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/FridaCause.java b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/FridaCause.java new file mode 100644 index 0000000000..a6de51b20a --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/FridaCause.java @@ -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; + } +} diff --git a/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/FridaCommand.java b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/FridaCommand.java new file mode 100644 index 0000000000..87114efd39 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/FridaCommand.java @@ -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 the type of object "returned" by the command + */ +public interface FridaCommand { + + /** + * 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); + +} diff --git a/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/FridaContext.java b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/FridaContext.java new file mode 100644 index 0000000000..8a1227ad0d --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/FridaContext.java @@ -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 map; + + public FridaContext(JsonObject cpuContext) { + this.map = new HashMap<>(); + for (Entry element : cpuContext.entrySet()) { + JsonPrimitive primitive = element.getValue().getAsJsonPrimitive(); + map.put(element.getKey(), primitive.getAsString()); + } + } + + public Map getChildren() { + return map; + } + +} diff --git a/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/FridaDebugger.java b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/FridaDebugger.java new file mode 100644 index 0000000000..9cac2c63e2 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/FridaDebugger.java @@ -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); + } + +} diff --git a/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/FridaError.java b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/FridaError.java new file mode 100644 index 0000000000..f377dd8c22 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/FridaError.java @@ -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(); + } + +} diff --git a/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/FridaEvent.java b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/FridaEvent.java new file mode 100644 index 0000000000..4a4e287ec9 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/FridaEvent.java @@ -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 { + /** + * 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(); + +} diff --git a/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/FridaEventsListener.java b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/FridaEventsListener.java new file mode 100644 index 0000000000..f4c9a6afb1 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/FridaEventsListener.java @@ -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); + +} diff --git a/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/FridaEventsListenerAdapter.java b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/FridaEventsListenerAdapter.java new file mode 100644 index 0000000000..51d6a9f843 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/FridaEventsListenerAdapter.java @@ -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 + } + +} diff --git a/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/FridaExport.java b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/FridaExport.java new file mode 100644 index 0000000000..339d64c977 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/FridaExport.java @@ -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; + } + +} diff --git a/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/FridaFileSpec.java b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/FridaFileSpec.java new file mode 100644 index 0000000000..42fb224f10 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/FridaFileSpec.java @@ -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; + } + +} diff --git a/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/FridaFrame.java b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/FridaFrame.java new file mode 100644 index 0000000000..30912c2320 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/FridaFrame.java @@ -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 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); + } + +} diff --git a/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/FridaFunction.java b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/FridaFunction.java new file mode 100644 index 0000000000..7d1b630df4 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/FridaFunction.java @@ -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 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; + } + +} diff --git a/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/FridaImport.java b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/FridaImport.java new file mode 100644 index 0000000000..678a84bd6d --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/FridaImport.java @@ -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; + } + +} diff --git a/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/FridaKernelMemoryRegionInfo.java b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/FridaKernelMemoryRegionInfo.java new file mode 100644 index 0000000000..3bc9850c52 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/FridaKernelMemoryRegionInfo.java @@ -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; + } + +} diff --git a/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/FridaKernelModule.java b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/FridaKernelModule.java new file mode 100644 index 0000000000..199aca0543 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/FridaKernelModule.java @@ -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 ""; + } + +} diff --git a/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/FridaManager.java b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/FridaManager.java new file mode 100644 index 0000000000..ddfe6cfe74 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/FridaManager.java @@ -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 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 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 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 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 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 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 addProcess(); + + /** + * Remove a process + * + * @param process the process to remove + * @return a future which completes then Frida has executed the command + */ + CompletableFuture removeProcess(FridaProcess process); + + /** + * Add a session + * + * @return a future which completes with the handle to the new process + */ + CompletableFuture 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 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 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 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> listProcesses(FridaSession session); + + /** + * List the available processes on target + * + * @return a future that completes with a list of PIDs + */ + CompletableFuture>> listAvailableProcesses(); + + /** + * List Frida's sessions + * + * @return a future that completes with a map of session IDs to session handles + */ + CompletableFuture> 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> 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> 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 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> 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> 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> 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> 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 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 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 setExceptionHandler(FridaProcess process); + + CompletableFuture attach(String pid); + + CompletableFuture launch(String fileName, List args); + + CompletableFuture launch(Map args); + + /********** NEEDED FOR TESTING ************/ + + /** + * Returns the current process + * + * @return the current process + */ + FridaProcess currentProcess(); + + CompletableFuture waitForPrompt(); + + CompletableFuture execute(FridaCommand cmd); + + DebugStatus processEvent(FridaEvent evt); + + DebugStatus getStatus(); + + void updateState(FridaSession session); + + FridaTarget getCurrentTarget(); + + void setCurrentTarget(FridaTarget target); + + CompletableFuture getSessionAttributes(FridaSession session); + + void enableDebugger(FridaSession session, int port); + +} diff --git a/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/FridaMemoryRegionInfo.java b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/FridaMemoryRegionInfo.java new file mode 100644 index 0000000000..315c49dc70 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/FridaMemoryRegionInfo.java @@ -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; + } + + +} diff --git a/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/FridaModule.java b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/FridaModule.java new file mode 100644 index 0000000000..b26a5e2906 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/FridaModule.java @@ -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; + } + +} diff --git a/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/FridaOS.java b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/FridaOS.java new file mode 100644 index 0000000000..0cf37d443b --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/FridaOS.java @@ -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()]; + } +} diff --git a/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/FridaPointer.java b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/FridaPointer.java new file mode 100644 index 0000000000..e83e7d7a16 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/FridaPointer.java @@ -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; + } +} diff --git a/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/FridaProcess.java b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/FridaProcess.java new file mode 100644 index 0000000000..07860439d8 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/FridaProcess.java @@ -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(); + } + +} diff --git a/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/FridaReason.java b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/FridaReason.java new file mode 100644 index 0000000000..a8ed2cadf8 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/FridaReason.java @@ -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(); +} diff --git a/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/FridaScript.java b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/FridaScript.java new file mode 100644 index 0000000000..08b7edcae7 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/FridaScript.java @@ -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; + } + +} diff --git a/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/FridaSection.java b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/FridaSection.java new file mode 100644 index 0000000000..85817a18a7 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/FridaSection.java @@ -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; + } + +} diff --git a/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/FridaSession.java b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/FridaSession.java new file mode 100644 index 0000000000..2378e9c428 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/FridaSession.java @@ -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 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 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; + } + +} diff --git a/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/FridaState.java b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/FridaState.java new file mode 100644 index 0000000000..629127bc73 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/FridaState.java @@ -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()]; + } + +} diff --git a/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/FridaStateListener.java b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/FridaStateListener.java new file mode 100644 index 0000000000..4fb992a97e --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/FridaStateListener.java @@ -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 { + /** + * 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); + } +} diff --git a/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/FridaSymbol.java b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/FridaSymbol.java new file mode 100644 index 0000000000..914f307e3c --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/FridaSymbol.java @@ -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; + } + +} diff --git a/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/FridaTarget.java b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/FridaTarget.java new file mode 100644 index 0000000000..c554c02e10 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/FridaTarget.java @@ -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(); + } + +} diff --git a/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/FridaThread.java b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/FridaThread.java new file mode 100644 index 0000000000..bfb921e656 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/FridaThread.java @@ -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()); + } + +} diff --git a/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/FridaValue.java b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/FridaValue.java new file mode 100644 index 0000000000..17fcbfab16 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/FridaValue.java @@ -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; + } + +} diff --git a/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/cmd/AbstractFridaCommand.java b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/cmd/AbstractFridaCommand.java new file mode 100644 index 0000000000..f330fa7261 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/cmd/AbstractFridaCommand.java @@ -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 the type of object "returned" by the command + */ +public abstract class AbstractFridaCommand implements FridaCommand { + 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; + } + +} diff --git a/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/cmd/FridaAddProcessCommand.java b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/cmd/FridaAddProcessCommand.java new file mode 100644 index 0000000000..2c9e0845c7 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/cmd/FridaAddProcessCommand.java @@ -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 { + + public FridaAddProcessCommand(FridaManagerImpl manager) { + super(manager); + } + + @Override + public FridaProcess complete(FridaPendingCommand pending) { + // Not apparent this is needed + return null; + } + +} diff --git a/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/cmd/FridaAddSessionCommand.java b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/cmd/FridaAddSessionCommand.java new file mode 100644 index 0000000000..79a780eb22 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/cmd/FridaAddSessionCommand.java @@ -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 { + + public FridaAddSessionCommand(FridaManagerImpl manager) { + super(manager); + } + + @Override + public FridaSession complete(FridaPendingCommand pending) { + // Not apparent this is needed + return null; + } + +} diff --git a/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/cmd/FridaAttachCommand.java b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/cmd/FridaAttachCommand.java new file mode 100644 index 0000000000..09a0284573 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/cmd/FridaAttachCommand.java @@ -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> { + + 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 complete(FridaPendingCommand pending) { + Set 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 + } +} diff --git a/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/cmd/FridaCommandError.java b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/cmd/FridaCommandError.java new file mode 100644 index 0000000000..071e46096c --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/cmd/FridaCommandError.java @@ -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; + } +} diff --git a/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/cmd/FridaConsoleExecCommand.java b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/cmd/FridaConsoleExecCommand.java new file mode 100644 index 0000000000..5553a8f2d1 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/cmd/FridaConsoleExecCommand.java @@ -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 { + 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); + } + +} diff --git a/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/cmd/FridaContinueCommand.java b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/cmd/FridaContinueCommand.java new file mode 100644 index 0000000000..c8871a096c --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/cmd/FridaContinueCommand.java @@ -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 { + + 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)); + } +} diff --git a/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/cmd/FridaDestroyCommand.java b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/cmd/FridaDestroyCommand.java new file mode 100644 index 0000000000..9c462f9dc0 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/cmd/FridaDestroyCommand.java @@ -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 { + 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(); + } +} diff --git a/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/cmd/FridaDetachCommand.java b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/cmd/FridaDetachCommand.java new file mode 100644 index 0000000000..d789f6fad0 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/cmd/FridaDetachCommand.java @@ -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 { + + 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 threads = manager.getKnownThreads(process); + List 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()); + } + } +} diff --git a/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/cmd/FridaGetSessionAttributesCommand.java b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/cmd/FridaGetSessionAttributesCommand.java new file mode 100644 index 0000000000..fad5726157 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/cmd/FridaGetSessionAttributesCommand.java @@ -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 { + + protected final FridaSession session; + private Map 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 entry : element.getAsJsonObject().entrySet()) { + attributes.put(entry.getKey(), entry.getValue().getAsString()); + } + } + +} diff --git a/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/cmd/FridaInterceptFunctionCommand.java b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/cmd/FridaInterceptFunctionCommand.java new file mode 100644 index 0000000000..868bc0bdb3 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/cmd/FridaInterceptFunctionCommand.java @@ -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 { + + private String address; + private Map arguments; + + public FridaInterceptFunctionCommand(FridaManagerImpl manager, String address, Map 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()); + } +} diff --git a/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/cmd/FridaKillCommand.java b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/cmd/FridaKillCommand.java new file mode 100644 index 0000000000..4184411c82 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/cmd/FridaKillCommand.java @@ -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 { + 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(); + } +} diff --git a/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/cmd/FridaLaunchProcessCommand.java b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/cmd/FridaLaunchProcessCommand.java new file mode 100644 index 0000000000..d51d99e8c0 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/cmd/FridaLaunchProcessCommand.java @@ -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 { + + private FridaProcessCreatedEvent created = null; + private boolean completed = false; + private String fileName; + private List args; + private List envp; + private List pathsIO; + private String wdir; + private long flags; + private boolean stopAtEntry; + + public FridaLaunchProcessCommand(FridaManagerImpl manager, String fileName, List args) { + this(manager, fileName, args, null, null, "", 0L, true); + } + + public FridaLaunchProcessCommand(FridaManagerImpl manager, String fileName, List args, + List envp, + List 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); + } +} diff --git a/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/cmd/FridaLaunchProcessWithOptionsCommand.java b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/cmd/FridaLaunchProcessWithOptionsCommand.java new file mode 100644 index 0000000000..f4d7ffb16a --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/cmd/FridaLaunchProcessWithOptionsCommand.java @@ -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 { + + private FridaProcessCreatedEvent created = null; + private boolean completed = false; + private String fileName; + private List args; + private List envp; + private List pathsIO; + private String wdir; + private long flags; + private boolean stopAtEntry; + + public FridaLaunchProcessWithOptionsCommand(FridaManagerImpl manager, Map args) { + super(manager); + this.fileName = (String) args.get("File"); + String argstr = (String) args.get("Args"); + this.args = argstr.equals("") ? new ArrayList() : Arrays.asList(argstr.split(" ")); + String envstr = (String) args.get("Env"); + this.envp = envstr.equals("") ? new ArrayList() : 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); + } +} diff --git a/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/cmd/FridaListAvailableProcessesCommand.java b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/cmd/FridaListAvailableProcessesCommand.java new file mode 100644 index 0000000000..26c8f6e4b0 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/cmd/FridaListAvailableProcessesCommand.java @@ -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>> { + + private List processList; + + public FridaListAvailableProcessesCommand(FridaManagerImpl manager) { + super(manager); + } + + @Override + public List> complete(FridaPendingCommand pending) { + List> result = new ArrayList<>(); + for (FridaProcess p : processList) { + result.add(new ImmutablePair(Long.toString(p.getPID()), p.getName())); + } + return result; + } + + @Override + public void invoke() { + processList = FridaEng.enumerateProcesses(manager.getCurrentTarget()); + } + +} diff --git a/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/cmd/FridaListHeapMemoryRegionsCommand.java b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/cmd/FridaListHeapMemoryRegionsCommand.java new file mode 100644 index 0000000000..56bbd738e5 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/cmd/FridaListHeapMemoryRegionsCommand.java @@ -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 { + + private FridaProcess process; + private List regions = new ArrayList<>(); + + public FridaListHeapMemoryRegionsCommand(FridaManagerImpl manager, FridaProcess process) { + super(manager); + this.process = process; + } + + /* + @Override + public List 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); + } + +} diff --git a/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/cmd/FridaListKernelMemoryRegionsCommand.java b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/cmd/FridaListKernelMemoryRegionsCommand.java new file mode 100644 index 0000000000..18912092cd --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/cmd/FridaListKernelMemoryRegionsCommand.java @@ -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 { + + private List 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); + } +} diff --git a/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/cmd/FridaListKernelModulesCommand.java b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/cmd/FridaListKernelModulesCommand.java new file mode 100644 index 0000000000..7c556603c7 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/cmd/FridaListKernelModulesCommand.java @@ -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 { + + private List 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); + } +} diff --git a/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/cmd/FridaListMemoryRegionsCommand.java b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/cmd/FridaListMemoryRegionsCommand.java new file mode 100644 index 0000000000..a04c63d136 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/cmd/FridaListMemoryRegionsCommand.java @@ -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 { + + private FridaProcess process; + private List 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); + } +} diff --git a/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/cmd/FridaListModuleExportsCommand.java b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/cmd/FridaListModuleExportsCommand.java new file mode 100644 index 0000000000..546a5bef39 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/cmd/FridaListModuleExportsCommand.java @@ -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> { + protected final FridaModule module; + private Map exports; + + public FridaListModuleExportsCommand(FridaManagerImpl manager, FridaModule module) { + super(manager); + this.module = module; + } + + @Override + public Map 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); + } + +} diff --git a/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/cmd/FridaListModuleImportsCommand.java b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/cmd/FridaListModuleImportsCommand.java new file mode 100644 index 0000000000..aabef58fc1 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/cmd/FridaListModuleImportsCommand.java @@ -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> { + protected final FridaModule module; + private Map imports; + + public FridaListModuleImportsCommand(FridaManagerImpl manager, FridaModule module) { + super(manager); + this.module = module; + } + + @Override + public Map 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); + } + +} diff --git a/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/cmd/FridaListModuleSectionsCommand.java b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/cmd/FridaListModuleSectionsCommand.java new file mode 100644 index 0000000000..ed2599bbac --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/cmd/FridaListModuleSectionsCommand.java @@ -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> { + protected final FridaModule module; + private Map sections; + + public FridaListModuleSectionsCommand(FridaManagerImpl manager, FridaModule module) { + super(manager); + this.module = module; + } + + @Override + public Map 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); + } + +} diff --git a/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/cmd/FridaListModuleSymbolsCommand.java b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/cmd/FridaListModuleSymbolsCommand.java new file mode 100644 index 0000000000..ca951279d8 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/cmd/FridaListModuleSymbolsCommand.java @@ -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> { + protected final FridaModule module; + private Map symbols; + + public FridaListModuleSymbolsCommand(FridaManagerImpl manager, FridaModule module) { + super(manager); + this.module = module; + } + + @Override + public Map 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); + } + +} diff --git a/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/cmd/FridaListModulesCommand.java b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/cmd/FridaListModulesCommand.java new file mode 100644 index 0000000000..f20da98ae3 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/cmd/FridaListModulesCommand.java @@ -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 { + + protected final FridaProcess process; + private List 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); + } +} diff --git a/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/cmd/FridaListProcessesCommand.java b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/cmd/FridaListProcessesCommand.java new file mode 100644 index 0000000000..eebd5fa2b0 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/cmd/FridaListProcessesCommand.java @@ -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> { + private Map updatedProcesses; + private FridaSession session; + + public FridaListProcessesCommand(FridaManagerImpl manager, FridaSession session) { + super(manager); + this.session = session; + } + + @Override + public Map complete(FridaPendingCommand pending) { + Map allProcesses = manager.getKnownProcesses(session); + Set 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); + } +} diff --git a/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/cmd/FridaListRegistersCommand.java b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/cmd/FridaListRegistersCommand.java new file mode 100644 index 0000000000..6874f7f831 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/cmd/FridaListRegistersCommand.java @@ -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> { + protected final FridaThread thread; + private Map result; + + public FridaListRegistersCommand(FridaManagerImpl manager, FridaThread thread) { + super(manager); + this.thread = thread; + } + + @Override + public Map complete(FridaPendingCommand pending) { + return result; + } + + @Override + public void invoke() { + FridaContext context = thread.getContext(); + result = context.getChildren(); + } +} diff --git a/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/cmd/FridaListSessionsCommand.java b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/cmd/FridaListSessionsCommand.java new file mode 100644 index 0000000000..731dfd117e --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/cmd/FridaListSessionsCommand.java @@ -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> { + private Map updatedSessions; + + public FridaListSessionsCommand(FridaManagerImpl manager) { + super(manager); + } + + @Override + public Map complete(FridaPendingCommand pending) { + Map allSessions = manager.getKnownSessions(); + Set 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(); + } + +} diff --git a/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/cmd/FridaListStackFramesCommand.java b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/cmd/FridaListStackFramesCommand.java new file mode 100644 index 0000000000..7ef0c29a30 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/cmd/FridaListStackFramesCommand.java @@ -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> { + protected final FridaThread thread; + private Map frames = new HashMap<>(); + private int frameCount = 0; + + public FridaListStackFramesCommand(FridaManagerImpl manager, FridaThread thread) { + super(manager); + this.thread = thread; + } + + @Override + public Map 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 map = new HashMap<>(); + for (Entry l : jobj.entrySet()) { + map.put(l.getKey(), l.getValue()); + } + FridaFrame frame = new FridaFrame(map, frameCount++); + frames.put(FridaClient.getId(frame), frame); + } +} diff --git a/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/cmd/FridaListThreadsCommand.java b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/cmd/FridaListThreadsCommand.java new file mode 100644 index 0000000000..b234f3d34e --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/cmd/FridaListThreadsCommand.java @@ -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 { + + protected final FridaProcess process; + private List 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); + } + +} diff --git a/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/cmd/FridaPendingCommand.java b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/cmd/FridaPendingCommand.java new file mode 100644 index 0000000000..c8889b2704 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/cmd/FridaPendingCommand.java @@ -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 the type "returned" by the command + */ +public class FridaPendingCommand extends CompletableFuture implements FridaCause { + private final FridaCommand cmd; + private final Set> evts = new LinkedHashSet<>(); + + /** + * Wrap a command for execution + * + * @param cmd the command + */ + public FridaPendingCommand(FridaCommand cmd) { + this.cmd = cmd; + } + + /** + * Get the command being executed + * + * @return cmd + */ + public FridaCommand 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 castSingleEvent(Class 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 findFirstOf(Class 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 > List findAllOf(Class cls) { + List 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 findSingleOf(Class cls) { + List 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 checkCompletion( + Class... classes) { + AbstractFridaCompletedCommandEvent completion = + findSingleOf(AbstractFridaCompletedCommandEvent.class); + // Allow query for exact class to override error interpretation + for (Class cls : classes) { + if (cls == completion.getClass()) { + return cls.cast(completion); + } + } + if (completion instanceof FridaCommandErrorEvent) { + throw new FridaCommandError(completion.getInfo(), cmd); + } + for (Class 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 + ")"; + } +} diff --git a/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/cmd/FridaReadKernelMemoryCommand.java b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/cmd/FridaReadKernelMemoryCommand.java new file mode 100644 index 0000000000..bbff8d6de3 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/cmd/FridaReadKernelMemoryCommand.java @@ -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> { + + 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 complete(FridaPendingCommand pending) { + RangeSet 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; + } + } + +} diff --git a/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/cmd/FridaReadMemoryCommand.java b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/cmd/FridaReadMemoryCommand.java new file mode 100644 index 0000000000..eb5bacadcb --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/cmd/FridaReadMemoryCommand.java @@ -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> { + + 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 complete(FridaPendingCommand pending) { + RangeSet 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; + } + } + +} diff --git a/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/cmd/FridaRemoveProcessCommand.java b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/cmd/FridaRemoveProcessCommand.java new file mode 100644 index 0000000000..dfe7c54435 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/cmd/FridaRemoveProcessCommand.java @@ -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 { + 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)); + } +} diff --git a/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/cmd/FridaRepeatableWatchMemoryCommand.java b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/cmd/FridaRepeatableWatchMemoryCommand.java new file mode 100644 index 0000000000..26250add08 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/cmd/FridaRepeatableWatchMemoryCommand.java @@ -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 { + + private Map arguments; + + public FridaRepeatableWatchMemoryCommand(FridaManagerImpl manager, Map 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()); + } +} diff --git a/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/cmd/FridaRequestActivationCommand.java b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/cmd/FridaRequestActivationCommand.java new file mode 100644 index 0000000000..ae5cd162d4 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/cmd/FridaRequestActivationCommand.java @@ -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 { + + 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); + } +} diff --git a/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/cmd/FridaRequestFocusCommand.java b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/cmd/FridaRequestFocusCommand.java new file mode 100644 index 0000000000..4b1428d2bc --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/cmd/FridaRequestFocusCommand.java @@ -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 { + + 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); + } +} diff --git a/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/cmd/FridaSetExceptionHandlerCommand.java b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/cmd/FridaSetExceptionHandlerCommand.java new file mode 100644 index 0000000000..e7ba238044 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/cmd/FridaSetExceptionHandlerCommand.java @@ -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 { + + 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);});"); + } + + +} diff --git a/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/cmd/FridaStalkThreadCommand.java b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/cmd/FridaStalkThreadCommand.java new file mode 100644 index 0000000000..0b0d545527 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/cmd/FridaStalkThreadCommand.java @@ -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.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 FridaStalkThreadCommand extends AbstractFridaCommand { + + private Map arguments; + private String tid; + + public FridaStalkThreadCommand(FridaManagerImpl manager, String tid, Map arguments) { + super(manager); + this.tid = tid; + this.arguments = arguments; + } + + @Override + public void invoke() { + String cmd = + "Stalker.follow(" + tid + ", {" + + " events: { " + + " call: " + arguments.get("EventCall") + "," + + " ret: " + arguments.get("EventRet") + "," + + " exec: " + arguments.get("EventExec") + "," + + " block: " + arguments.get("EventBlock") + "," + + " compile: " + arguments.get("EventCompile") + + " }, "; + try { + String onReceive = (String) arguments.get("OnReceive"); + String onCallSummary = (String) arguments.get("OnCallSummary"); + FileInputStream fis; + if (!onReceive.isEmpty()) { + fis = new FileInputStream(new File(onReceive)); + } else { + fis = new FileInputStream(new File(onCallSummary)); + } + byte[] bytes = fis.readAllBytes(); + String str = new String(bytes); + cmd += str + "});"; + } + catch (IOException e) { + e.printStackTrace(); + return; + } + + manager.loadPermanentScript(this, (String) arguments.get("Name"), cmd); + } + + @Override + public void parseSpecifics(JsonElement element) { + Msg.info(this, element); + } +} diff --git a/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/cmd/FridaWatchMemoryCommand.java b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/cmd/FridaWatchMemoryCommand.java new file mode 100644 index 0000000000..06fbb278c7 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/cmd/FridaWatchMemoryCommand.java @@ -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.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 FridaWatchMemoryCommand extends AbstractFridaCommand { + + private Map arguments; + + public FridaWatchMemoryCommand(FridaManagerImpl manager, Map 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 = "MemoryAccessMonitor.enable(" + + "{ base: ptr(" + addr + "), " + + " size: " + size + " }, " + + "{ "; + 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 + "});"; + } + catch (IOException e) { + e.printStackTrace(); + return; + } + + manager.loadPermanentScript(this, (String) arguments.get("Name"), cmd); + } + + @Override + public void parseSpecifics(JsonElement element) { + Msg.info(this, element); + manager.unloadPermanentScript(getName()); + } +} diff --git a/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/cmd/FridaWriteKernelMemoryCommand.java b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/cmd/FridaWriteKernelMemoryCommand.java new file mode 100644 index 0000000000..7ce0706341 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/cmd/FridaWriteKernelMemoryCommand.java @@ -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.nio.ByteBuffer; + +import agent.frida.manager.impl.FridaManagerImpl; +import ghidra.program.model.address.Address; + +public class FridaWriteKernelMemoryCommand extends AbstractFridaCommand { + + private final Address addr; + private final ByteBuffer buf; + private final int len; + + public FridaWriteKernelMemoryCommand(FridaManagerImpl manager, Address addr, ByteBuffer buf, int len) { + super(manager); + this.addr = addr; + this.buf = buf.duplicate(); + this.len = len; + } + + @Override + public void invoke() { + //TODO: This is completely untested and probably will not work + String bufstr = buf.toString(); + manager.loadScript(this, "write_memory", + "var buf = []; " + + "var str = '" + bufstr + "';" + + "var len = " + len + ";" + + "for (var i = 0; i < len; ++i) {" + + " var code = str.charCodeAt(i);" + + " buf = buf.concat([code]);" + + "}" + + "Kernel.writeByteArray(ptr(0x"+addr+")buf);"); + } + +} diff --git a/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/cmd/FridaWriteMemoryCommand.java b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/cmd/FridaWriteMemoryCommand.java new file mode 100644 index 0000000000..67bf783804 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/cmd/FridaWriteMemoryCommand.java @@ -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.nio.ByteBuffer; + +import agent.frida.manager.impl.FridaManagerImpl; +import ghidra.program.model.address.Address; + +public class FridaWriteMemoryCommand extends AbstractFridaCommand { + + private final Address addr; + private final ByteBuffer buf; + private final int len; + + public FridaWriteMemoryCommand(FridaManagerImpl manager, Address addr, ByteBuffer buf, int len) { + super(manager); + this.addr = addr; + this.buf = buf.duplicate(); + this.len = len; + } + + @Override + public void invoke() { + //TODO: This is completely untested and probably will not work + String bufstr = buf.toString(); + manager.loadScript(this, "write_memory", + "var buf = []; " + + "var str = '" + bufstr + "';" + + "var len = " + len + ";" + + "for (var i = 0; i < len; ++i) {" + + " var code = str.charCodeAt(i);" + + " buf = buf.concat([code]);" + + "}" + + "ptr(0x"+addr+").writeByteArray(buf);"); + } + +} diff --git a/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/evt/AbstractFridaCompletedCommandEvent.java b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/evt/AbstractFridaCompletedCommandEvent.java new file mode 100644 index 0000000000..5ea7631360 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/evt/AbstractFridaCompletedCommandEvent.java @@ -0,0 +1,34 @@ +/* ### + * 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.evt; + +import agent.frida.manager.FridaState; + +public class AbstractFridaCompletedCommandEvent extends AbstractFridaEvent { + + public AbstractFridaCompletedCommandEvent(String message) { + super(message); + } + + public AbstractFridaCompletedCommandEvent() { + super(null); + } + + public FridaState newState() { + return null; + } + +} diff --git a/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/evt/AbstractFridaEvent.java b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/evt/AbstractFridaEvent.java new file mode 100644 index 0000000000..4ad0fefa11 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/evt/AbstractFridaEvent.java @@ -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.evt; + +import agent.frida.manager.FridaCause; +import agent.frida.manager.FridaCause.Causes; +import agent.frida.manager.FridaEvent; +import agent.frida.manager.FridaReason; +import agent.frida.manager.FridaState; +import agent.frida.manager.cmd.FridaPendingCommand; + +/** + * A base class for frida events + * + * @param the type of information detailing the event + */ +public abstract class AbstractFridaEvent implements FridaEvent { + private final T info; + protected FridaCause cause = Causes.UNCLAIMED; + protected boolean stolen = false; + //protected DebugStatus status = DebugStatus.NO_CHANGE; + + /** + * Construct a new event with the given information + * + * @param info the information + */ + protected AbstractFridaEvent(T info) { + this.info = info; + } + + @Override + public T getInfo() { + return info; + } + + @Override + public void claim(FridaPendingCommand cmd) { + cause = cmd; + } + + @Override + public FridaCause getCause() { + return cause; + } + + public FridaReason getReason() { + return FridaReason.getReason(null); + } + + @Override + public void steal() { + stolen = true; + } + + @Override + public boolean isStolen() { + return stolen; + } + + @Override + public String toString() { + return "<" + getClass().getSimpleName() + " " + info + " >"; + } + + @Override + public FridaState newState() { + return null; + } + +} diff --git a/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/evt/FridaCommandDoneEvent.java b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/evt/FridaCommandDoneEvent.java new file mode 100644 index 0000000000..ae1fbd2873 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/evt/FridaCommandDoneEvent.java @@ -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.evt; + +import agent.frida.manager.FridaCommand; +import agent.frida.manager.FridaState; + +public class FridaCommandDoneEvent extends AbstractFridaCompletedCommandEvent { + + private FridaCommand cmd; + + /** + * Construct a new event, parsing the tail for information + */ + public FridaCommandDoneEvent() { + super(); + } + + public FridaCommandDoneEvent(FridaCommand cmd) { + super(cmd.toString()); + this.cmd = cmd; + } + + @Override + public FridaState newState() { + return FridaState.FRIDA_THREAD_STOPPED; + } + + public FridaCommand getCmd() { + return cmd; + } + +} diff --git a/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/evt/FridaCommandErrorEvent.java b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/evt/FridaCommandErrorEvent.java new file mode 100644 index 0000000000..43b007bed2 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/evt/FridaCommandErrorEvent.java @@ -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.evt; + +import agent.frida.manager.FridaEvent; +import agent.frida.manager.FridaState; + +public class FridaCommandErrorEvent extends AbstractFridaCompletedCommandEvent { + + /** + * Construct a new event using the given error message + * + * @param message the message + * @return the new event + */ + public static FridaEvent fromMessage(String message) { + return new FridaCommandErrorEvent(message); + } + + protected FridaCommandErrorEvent() { + super(); + } + + protected FridaCommandErrorEvent(String message) { + super(message); + } + + @Override + public FridaState newState() { + return FridaState.FRIDA_THREAD_STOPPED; + } +} diff --git a/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/evt/FridaConsoleOutputEvent.java b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/evt/FridaConsoleOutputEvent.java new file mode 100644 index 0000000000..3433952ee4 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/evt/FridaConsoleOutputEvent.java @@ -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.manager.evt; + +import agent.frida.frida.FridaProcessInfo; + +/** + * The event corresponding with FridaProcess.eBroadcastBitSTDERR & FridaProcess.eBroadcastBitSTDOUT + */ +public class FridaConsoleOutputEvent extends AbstractFridaEvent { + + private int mask; + private String text; + + public FridaConsoleOutputEvent(int mask, String text) { + super(null); + this.mask = mask; + this.text = text; + } + + public String getOutput() { + return text; + } + + public int getMask() { + return mask; + } +} diff --git a/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/evt/FridaInterruptEvent.java b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/evt/FridaInterruptEvent.java new file mode 100644 index 0000000000..316d32a3ee --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/evt/FridaInterruptEvent.java @@ -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.evt; + +import agent.frida.frida.FridaProcessInfo; + +public class FridaInterruptEvent extends AbstractFridaEvent { + + public FridaInterruptEvent(FridaProcessInfo info) { + super(info); + } + +} diff --git a/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/evt/FridaMemoryRegionAddedEvent.java b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/evt/FridaMemoryRegionAddedEvent.java new file mode 100644 index 0000000000..7d0f943a53 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/evt/FridaMemoryRegionAddedEvent.java @@ -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.evt; + +import agent.frida.frida.FridaRegionInfo; + +/** + * The event corresponding with SBTarget.eBroadcastBitModulesLoaded + */ +public class FridaMemoryRegionAddedEvent extends AbstractFridaEvent { + + public FridaMemoryRegionAddedEvent(FridaRegionInfo info) { + super(info); + } +} diff --git a/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/evt/FridaMemoryRegionRemovedEvent.java b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/evt/FridaMemoryRegionRemovedEvent.java new file mode 100644 index 0000000000..ea5e56ba2e --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/evt/FridaMemoryRegionRemovedEvent.java @@ -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.evt; + +import agent.frida.frida.FridaRegionInfo; + +/** + * The event corresponding with SBTarget.eBroadcastBitModulesUnloaded + */ +public class FridaMemoryRegionRemovedEvent extends AbstractFridaEvent { + + public FridaMemoryRegionRemovedEvent(FridaRegionInfo info) { + super(info); + } +} diff --git a/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/evt/FridaMemoryRegionReplacedEvent.java b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/evt/FridaMemoryRegionReplacedEvent.java new file mode 100644 index 0000000000..9815eeb1ea --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/evt/FridaMemoryRegionReplacedEvent.java @@ -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.evt; + +import agent.frida.frida.FridaRegionInfo; + +/** + * The event corresponding with SBTarget.eBroadcastBitModulesLoaded + */ +public class FridaMemoryRegionReplacedEvent extends AbstractFridaEvent { + + public FridaMemoryRegionReplacedEvent(FridaRegionInfo info) { + super(info); + } +} diff --git a/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/evt/FridaModuleLoadedEvent.java b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/evt/FridaModuleLoadedEvent.java new file mode 100644 index 0000000000..65c08ceaa8 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/evt/FridaModuleLoadedEvent.java @@ -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.evt; + +import agent.frida.frida.FridaModuleInfo; + +/** + * The event corresponding with SBTarget.eBroadcastBitModulesLoaded + */ +public class FridaModuleLoadedEvent extends AbstractFridaEvent { + + public FridaModuleLoadedEvent(FridaModuleInfo info) { + super(info); + } +} diff --git a/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/evt/FridaModuleReplacedEvent.java b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/evt/FridaModuleReplacedEvent.java new file mode 100644 index 0000000000..9b586bed34 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/evt/FridaModuleReplacedEvent.java @@ -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.evt; + +import agent.frida.frida.FridaModuleInfo; + +/** + * The event corresponding with SBTarget.eBroadcastBitModulesLoaded + */ +public class FridaModuleReplacedEvent extends AbstractFridaEvent { + + public FridaModuleReplacedEvent(FridaModuleInfo info) { + super(info); + } +} diff --git a/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/evt/FridaModuleUnloadedEvent.java b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/evt/FridaModuleUnloadedEvent.java new file mode 100644 index 0000000000..c8ecf9a844 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/evt/FridaModuleUnloadedEvent.java @@ -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.evt; + +import agent.frida.frida.FridaModuleInfo; + +/** + * The event corresponding with SBTarget.eBroadcastBitModulesUnloaded + */ +public class FridaModuleUnloadedEvent extends AbstractFridaEvent { + + public FridaModuleUnloadedEvent(FridaModuleInfo info) { + super(info); + } +} diff --git a/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/evt/FridaProcessCreatedEvent.java b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/evt/FridaProcessCreatedEvent.java new file mode 100644 index 0000000000..2efd1774d4 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/evt/FridaProcessCreatedEvent.java @@ -0,0 +1,32 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.frida.manager.evt; + +import agent.frida.frida.FridaProcessInfo; +import agent.frida.manager.FridaState; + +public class FridaProcessCreatedEvent extends AbstractFridaEvent { + + public FridaProcessCreatedEvent(FridaProcessInfo info) { + super(info); + } + + @Override + public FridaState newState() { + return FridaState.FRIDA_THREAD_UNINTERRUPTIBLE; + } + +} diff --git a/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/evt/FridaProcessExitedEvent.java b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/evt/FridaProcessExitedEvent.java new file mode 100644 index 0000000000..cf98b97b48 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/evt/FridaProcessExitedEvent.java @@ -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.manager.evt; + +import agent.frida.manager.FridaState; + +public class FridaProcessExitedEvent extends AbstractFridaEvent { + + public FridaProcessExitedEvent(Integer exitCode) { + super(exitCode); + } + + @Override + public FridaState newState() { + return FridaState.FRIDA_THREAD_HALTED; + } + +} diff --git a/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/evt/FridaProcessReplacedEvent.java b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/evt/FridaProcessReplacedEvent.java new file mode 100644 index 0000000000..6f251f5dba --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/evt/FridaProcessReplacedEvent.java @@ -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.evt; + +import agent.frida.frida.FridaProcessInfo; +import agent.frida.manager.FridaState; + +public class FridaProcessReplacedEvent extends AbstractFridaEvent { + + public FridaProcessReplacedEvent(FridaProcessInfo info) { + super(info); + } + + @Override + public FridaState newState() { + // NB: it's very tempting to relay the info we have, but + // doing so fouls up a lot of the tests because the stopped + // message arrives ahead of breakpointHit + + //DebugProcessInfo pinfo = (DebugProcessInfo) getInfo(); + //return pinfo.process.GetState(); + return null; + } + +} diff --git a/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/evt/FridaProcessSelectedEvent.java b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/evt/FridaProcessSelectedEvent.java new file mode 100644 index 0000000000..4844d19bce --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/evt/FridaProcessSelectedEvent.java @@ -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.evt; + +import agent.frida.frida.FridaClient; +import agent.frida.manager.FridaProcess; + +public class FridaProcessSelectedEvent extends AbstractFridaEvent { + private final String id; + private FridaProcess process; + + public FridaProcessSelectedEvent(FridaProcess process) { + super(FridaClient.getId(process)); + this.process = process; + this.id = FridaClient.getId(process); + } + + /** + * Get the selected process ID + * + * @return the process ID + */ + public String getProcessId() { + return id; + } + + public FridaProcess getProcess() { + return process; + } + +} diff --git a/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/evt/FridaRunningEvent.java b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/evt/FridaRunningEvent.java new file mode 100644 index 0000000000..96bc3418a0 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/evt/FridaRunningEvent.java @@ -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.manager.evt; + +import agent.frida.manager.FridaState; + +public class FridaRunningEvent extends AbstractFridaEvent { + private final String id; + + /** + * Construct a new event, parsing the tail for information + * + * A thread ID must be specified by frida. + * + * @param id the event info + */ + public FridaRunningEvent(String id) { + super(id); + this.id = id; + } + + /** + * Get the ID of the thread causing the event + * + * @return the thread ID + */ + public String getThreadId() { + return id; + } + + @Override + public FridaState newState() { + return FridaState.FRIDA_THREAD_RUNNING; + } + +} diff --git a/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/evt/FridaSelectedFrameChangedEvent.java b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/evt/FridaSelectedFrameChangedEvent.java new file mode 100644 index 0000000000..3a01dd9c94 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/evt/FridaSelectedFrameChangedEvent.java @@ -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.evt; + +import agent.frida.frida.FridaClient; +import agent.frida.frida.FridaThreadInfo; +import agent.frida.manager.*; + +/** + * The event corresponding with FridaThread.eBroadcastBitSelectedFrameChanged + */ +public class FridaSelectedFrameChangedEvent extends AbstractFridaEvent { + private final String id; + private FridaState state; + private FridaThread thread; + private FridaFrame frame; + + /** + * @param info thread info + */ + public FridaSelectedFrameChangedEvent(FridaThreadInfo info) { + super(FridaClient.getId(info.thread)); + this.id = FridaClient.getId(thread); + this.state = info.thread.getState(); + this.thread = info.thread; + } + + /** + * Get the selected thread ID + * + * @return the thread ID + */ + public String getThreadId() { + return id; + } + + public FridaState getState() { + return state; + } + + public FridaThread getThread() { + return thread; + } + + public FridaFrame getFrame() { + return frame; + } + +} diff --git a/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/evt/FridaSessionCreatedEvent.java b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/evt/FridaSessionCreatedEvent.java new file mode 100644 index 0000000000..3653584294 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/evt/FridaSessionCreatedEvent.java @@ -0,0 +1,25 @@ +/* ### + * 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.evt; + +import agent.frida.frida.FridaSessionInfo; + +public class FridaSessionCreatedEvent extends AbstractFridaEvent { + + public FridaSessionCreatedEvent(FridaSessionInfo info) { + super(info); + } +} diff --git a/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/evt/FridaSessionExitedEvent.java b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/evt/FridaSessionExitedEvent.java new file mode 100644 index 0000000000..b8fe1b4ef2 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/evt/FridaSessionExitedEvent.java @@ -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.evt; + +import agent.frida.manager.FridaState; + +public class FridaSessionExitedEvent extends AbstractFridaEvent { + + public String sessionId; + public Integer exitCode; + + public FridaSessionExitedEvent(String sessionId, Integer exitCode) { + super(exitCode); + this.sessionId = sessionId; + this.exitCode = exitCode; + } + + @Override + public FridaState newState() { + return FridaState.FRIDA_THREAD_HALTED; + } + +} diff --git a/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/evt/FridaSessionReplacedEvent.java b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/evt/FridaSessionReplacedEvent.java new file mode 100644 index 0000000000..087f701c9c --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/evt/FridaSessionReplacedEvent.java @@ -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.evt; + +import agent.frida.frida.FridaSessionInfo; + +public class FridaSessionReplacedEvent extends AbstractFridaEvent { + + public FridaSessionReplacedEvent(FridaSessionInfo next) { + super(next); + } + +} diff --git a/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/evt/FridaSessionSelectedEvent.java b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/evt/FridaSessionSelectedEvent.java new file mode 100644 index 0000000000..3989164eec --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/evt/FridaSessionSelectedEvent.java @@ -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.manager.evt; + +import agent.frida.frida.FridaClient; +import agent.frida.manager.FridaSession; + +public class FridaSessionSelectedEvent extends AbstractFridaEvent { + private final String id; + private FridaSession session; + + /** + * The selected session ID must be specified by Frida. + * + * @param session Frida-defined session + */ + public FridaSessionSelectedEvent(FridaSession session) { + super(FridaClient.getId(session)); + this.session = session; + this.id = FridaClient.getId(session); + } + + /** + * Get the selected session ID + * + * @return the session ID + */ + public String getSessionId() { + return id; + } + + public FridaSession getSession() { + return session; + } + +} diff --git a/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/evt/FridaStateChangedEvent.java b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/evt/FridaStateChangedEvent.java new file mode 100644 index 0000000000..a8cfb426d2 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/evt/FridaStateChangedEvent.java @@ -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.evt; + +import agent.frida.frida.FridaClient; +import agent.frida.manager.*; +import ghidra.dbg.target.TargetExecutionStateful.TargetExecutionState; + +/** + * The event corresponding with FridaProcess.eBroadcastBitStateChanged + */ +public class FridaStateChangedEvent extends AbstractFridaEvent { + + private FridaState state = null; + private String id; + + public FridaStateChangedEvent(Object obj, TargetExecutionState state) { + super(state); + this.state = FridaClient.convertState(state); + this.id = FridaClient.getId(obj); + } + + public FridaFrame getFrame(FridaThread thread) { + return null; + } + + @Override + public FridaState newState() { + return state; + } + + public void setState(FridaState state) { + this.state = state; + } + + public String getId() { + return id; + } + +} diff --git a/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/evt/FridaStoppedEvent.java b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/evt/FridaStoppedEvent.java new file mode 100644 index 0000000000..823dfb3370 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/evt/FridaStoppedEvent.java @@ -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.evt; + +import agent.frida.manager.FridaFrame; +import agent.frida.manager.FridaThread; +import agent.frida.manager.FridaState; + +public class FridaStoppedEvent extends AbstractFridaEvent { + private final String id; + + /** + * Construct a new event, parsing the tail for information + * + * A thread ID must be specified by frida. + * + * @param id the event info + */ + public FridaStoppedEvent(String id) { + super(id); + this.id = id; + } + + /** + * Get the ID of the thread causing the event + * + * @return the thread ID + */ + public String getThreadId() { + return id; + } + + /** + * Get the current frame, if applicable + * + * @param thread the current thread + * @return the frame + */ + public FridaFrame getFrame(FridaThread thread) { + return null; + } + + @Override + public FridaState newState() { + return FridaState.FRIDA_THREAD_STOPPED; + } +} diff --git a/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/evt/FridaThreadCreatedEvent.java b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/evt/FridaThreadCreatedEvent.java new file mode 100644 index 0000000000..9e7598219b --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/evt/FridaThreadCreatedEvent.java @@ -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.evt; + +import agent.frida.frida.FridaThreadInfo; + +public class FridaThreadCreatedEvent extends AbstractFridaEvent { + + public FridaThreadCreatedEvent(FridaThreadInfo info) { + super(info); + } + +} diff --git a/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/evt/FridaThreadExitedEvent.java b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/evt/FridaThreadExitedEvent.java new file mode 100644 index 0000000000..2b2684301c --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/evt/FridaThreadExitedEvent.java @@ -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. + */ +package agent.frida.manager.evt; + +public class FridaThreadExitedEvent extends AbstractFridaEvent { + + public FridaThreadExitedEvent(Integer exitCode) { + super(exitCode); + } + +} diff --git a/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/evt/FridaThreadReplacedEvent.java b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/evt/FridaThreadReplacedEvent.java new file mode 100644 index 0000000000..64d55f8910 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/evt/FridaThreadReplacedEvent.java @@ -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.evt; + +import agent.frida.frida.FridaThreadInfo; + +public class FridaThreadReplacedEvent extends AbstractFridaEvent { + + public FridaThreadReplacedEvent(FridaThreadInfo info) { + super(info); + } + +} diff --git a/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/evt/FridaThreadResumedEvent.java b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/evt/FridaThreadResumedEvent.java new file mode 100644 index 0000000000..b0255ba084 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/evt/FridaThreadResumedEvent.java @@ -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.evt; + +import agent.frida.frida.FridaThreadInfo; + +/** + * The event corresponding with SBThread.eBroadcastBitThreadResumed + */ +public class FridaThreadResumedEvent extends AbstractFridaEvent { + + public FridaThreadResumedEvent(FridaThreadInfo info) { + super(info); + } +} diff --git a/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/evt/FridaThreadSelectedEvent.java b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/evt/FridaThreadSelectedEvent.java new file mode 100644 index 0000000000..a55a8bf3e0 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/evt/FridaThreadSelectedEvent.java @@ -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.evt; + +import agent.frida.frida.FridaClient; +import agent.frida.frida.FridaThreadInfo; +import agent.frida.manager.*; + +/** + * The event corresponding with FridaThread.eBroadcastBitThreadSelected + */ +public class FridaThreadSelectedEvent extends AbstractFridaEvent { + private final String id; + private FridaState state; + private FridaThread thread; + private FridaFrame frame; + + /** + * @param info thread info + */ + public FridaThreadSelectedEvent(FridaThreadInfo info) { + super(FridaClient.getId(info.thread)); + this.id = FridaClient.getId(info.thread); + this.state = info.thread.getState(); + this.thread = info.thread; + } + + /** + * Get the selected thread ID + * + * @return the thread ID + */ + public String getThreadId() { + return id; + } + + public FridaState getState() { + return state; + } + + public FridaThread getThread() { + return thread; + } + + public FridaFrame getFrame() { + return frame; + } + +} diff --git a/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/evt/FridaThreadStackChangedEvent.java b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/evt/FridaThreadStackChangedEvent.java new file mode 100644 index 0000000000..21f5c631d0 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/evt/FridaThreadStackChangedEvent.java @@ -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.evt; + +import agent.frida.frida.FridaThreadInfo; + +/** + * The event corresponding with SBThread.eBroadcastBitStackChanged + */ +public class FridaThreadStackChangedEvent extends AbstractFridaEvent { + + public FridaThreadStackChangedEvent(FridaThreadInfo info) { + super(info); + } +} diff --git a/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/evt/FridaThreadSuspendedEvent.java b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/evt/FridaThreadSuspendedEvent.java new file mode 100644 index 0000000000..40f7c4949e --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/evt/FridaThreadSuspendedEvent.java @@ -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.evt; + +import agent.frida.frida.FridaThreadInfo; + +/** + * The event corresponding with SBThread.eBroadcastBitThreadSuspended + */ +public class FridaThreadSuspendedEvent extends AbstractFridaEvent { + + public FridaThreadSuspendedEvent(FridaThreadInfo info) { + super(info); + } +} diff --git a/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/impl/FridaManagerImpl.java b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/impl/FridaManagerImpl.java new file mode 100644 index 0000000000..dbc46df2af --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/manager/impl/FridaManagerImpl.java @@ -0,0 +1,1300 @@ +/* ### + * 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.impl; + +import static ghidra.async.AsyncUtils.*; + +import java.util.*; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicReference; + +import org.apache.commons.lang3.tuple.Pair; + +import com.google.gson.JsonElement; +import com.sun.jna.NativeLong; +import com.sun.jna.Pointer; + +import agent.frida.frida.*; +import agent.frida.frida.FridaClient.DebugEndSessionFlags; +import agent.frida.frida.FridaClient.DebugStatus; +import agent.frida.gadp.impl.AbstractClientThreadExecutor; +import agent.frida.gadp.impl.FridaClientThreadExecutor; +import agent.frida.manager.*; +import agent.frida.manager.FridaCause.Causes; +import agent.frida.manager.cmd.*; +import agent.frida.manager.evt.*; +import agent.frida.model.iface1.*; +import ghidra.async.*; +import ghidra.dbg.target.TargetObject; +import ghidra.dbg.util.HandlerMap; +import ghidra.lifecycle.Internal; +import ghidra.util.Msg; +import ghidra.util.datastruct.ListenerSet; + +public class FridaManagerImpl implements FridaManager { + + public DebugStatus status; + + protected AbstractClientThreadExecutor executor; + protected FridaClientReentrant reentrantClient; + + private List> activeCmds = new ArrayList<>(); + + protected final Map sessions = new LinkedHashMap<>(); + protected FridaTarget curSession = null; + private final Map unmodifiableSessions = + Collections.unmodifiableMap(sessions); + + protected final Map> processes = new LinkedHashMap<>(); + protected final Map> threads = new LinkedHashMap<>(); + protected final Map> modules = new LinkedHashMap<>(); + protected final Map> regions = new LinkedHashMap<>(); + + protected final Map kmodules = new LinkedHashMap<>(); + protected final Map kregions = new LinkedHashMap<>(); + + protected final AsyncReference state = new AsyncReference<>(null); + private final HandlerMap, Void, DebugStatus> handlerMap = new HandlerMap<>(); + private final Map, DebugStatus> statusMap = new LinkedHashMap<>(); + private final ListenerSet listenersEvent = + new ListenerSet<>(FridaEventsListener.class); + + private FridaTarget currentTarget; + private FridaSession currentSession; + private FridaProcess currentProcess; + private FridaThread currentThread; + private volatile boolean waiting = false; + private boolean kernelMode = false; + private CompletableFuture continuation; + + private Map scripts = new HashMap<>(); + + /** + * Instantiate a new manager + */ + public FridaManagerImpl() { + defaultHandlers(); + } + + /** + * @param processId the process ID to remove + * @param id the thread ID to remove + */ + public void removeThread(String processId, String id) { + synchronized (threads) { + if (threads.get(processId).remove(id) == null) { + throw new IllegalArgumentException("There is no thread with id " + id); + } + } + } + + @Override + public FridaThread getThread(FridaProcess process, String tid) { + synchronized (threads) { + return threads.get(FridaClient.getId(process)).get(tid); + } + } + + public void addThreadIfAbsent(FridaProcess process, FridaThread thread) { + synchronized (threads) { + Map map = threads.get(FridaClient.getId(process)); + if (map == null) { + map = new HashMap<>(); + threads.put(FridaClient.getId(process), map); + } + if (thread == null) { + return; + } + String id = FridaClient.getId(thread); + FridaThread pred = map.get(id); + if (!map.containsKey(id) || !thread.equals(pred)) { + FridaThreadInfo info = new FridaThreadInfo(thread); + if (!map.containsKey(id)) { + getClient().processEvent(new FridaThreadCreatedEvent(info)); + } + else { + getClient().processEvent(new FridaThreadReplacedEvent(info)); + } + map.put(id, thread); + } + } + } + + /** + * @param sessionId the session ID to remove + * @param id the process ID to remove + * @param cause the cause of removal + */ + @Internal + public void removeProcess(String sessionId, String id, FridaCause cause) { + synchronized (processes) { + FridaProcess proc = processes.get(sessionId).remove(id); + if (proc == null) { + throw new IllegalArgumentException("There is no process with id " + id); + } + Set toRemove = new HashSet<>(); + String processId = FridaClient.getId(proc); + for (String tid : threads.get(processId).keySet()) { + FridaThread thread = threads.get(processId).get(tid); + String pid = FridaClient.getId(thread.getProcess()); + if (pid.equals(id)) { + toRemove.add(tid); + } + } + for (String tid : toRemove) { + removeThread(processId, tid); + } + getEventListeners().fire.processRemoved(id, cause); + } + } + + /** + * Update the selected process + * + * @param session wrapper for Frida pointer + * @param id process pid + * @return success status + */ + @Override + public FridaProcess getProcess(FridaSession session, String id) { + synchronized (processes) { + String sessionId = FridaClient.getId(session); + FridaProcess result = processes.get(sessionId).get(id); + if (result == null) { + throw new IllegalArgumentException("There is no process with id " + id); + } + return result; + } + } + + public void addProcessIfAbsent(FridaSession session, FridaProcess process) { + synchronized (processes) { + String sessionId = FridaClient.getId(session); + Map map = processes.get(sessionId); + if (map == null) { + map = new HashMap<>(); + processes.put(sessionId, map); + } + String id = FridaClient.getId(process); + FridaProcess pred = map.get(id); + if (!map.containsKey(id) || !process.equals(pred)) { + FridaProcessInfo info = new FridaProcessInfo(process); + if (!map.containsKey(id)) { + getClient().processEvent(new FridaProcessCreatedEvent(info)); + } + else { + getClient().processEvent(new FridaProcessReplacedEvent(info)); + } + map.put(id, process); + } + } + } + + /** + * @param id the session ID to remove + * @param cause the cause of removal + */ + @Internal + public void removeSession(String id, FridaCause cause) { + synchronized (sessions) { + if (sessions.remove(id) == null) { + throw new IllegalArgumentException("There is no session with id " + id); + } + getEventListeners().fire.sessionRemoved(id, cause); + } + } + + @Override + public FridaSession getSession(String id) { + synchronized (sessions) { + FridaSession result = sessions.get(id); + if (result == null) { + throw new IllegalArgumentException("There is no session with id " + id); + } + return result; + } + } + + public void addSessionIfAbsent(FridaSession session) { + synchronized (sessions) { + String id = FridaClient.getId(session); + FridaSession pred = sessions.get(id); + if (!sessions.containsKey(id) || !session.equals(pred)) { + FridaSessionInfo info = new FridaSessionInfo(session); + if (sessions.containsKey(id)) { + //removeSession(sessions.get(id)); + getClient().processEvent(new FridaSessionReplacedEvent(info)); + } + else { + getClient().processEvent(new FridaSessionCreatedEvent(info)); + } + sessions.put(id, session); + } + } + } + + /** + * @param process wrapper for Frida pointer + * @param id the module name to remove + */ + public void removeModule(FridaProcess process, String id) { + synchronized (modules) { + if (modules.get(FridaClient.getId(process)).remove(id) == null) { + throw new IllegalArgumentException("There is no module with id " + id); + } + } + } + + @Override + public FridaModule getModule(FridaProcess process, String id) { + synchronized (modules) { + return modules.get(FridaClient.getId(process)).get(id); + } + } + + public void addModuleIfAbsent(FridaProcess process, FridaModule module) { + synchronized (modules) { + String sessionId = FridaClient.getId(process); + Map map = modules.get(sessionId); + if (map == null) { + map = new HashMap<>(); + modules.put(sessionId, map); + } + if (module == null) { + return; + } + String id = FridaClient.getId(module); + FridaModule pred = map.get(id); + if (!map.containsKey(id) || !module.equals(pred)) { + FridaModuleInfo info = new FridaModuleInfo(process, module); + if (!map.containsKey(id)) { + getClient().processEvent(new FridaModuleLoadedEvent(info)); + } + else { + getClient().processEvent(new FridaModuleReplacedEvent(info)); + } + map.put(id, module); + } + } + } + + public void addKernelModuleIfAbsent(FridaKernelModule module) { + synchronized (kmodules) { + if (module == null) { + return; + } + String id = FridaClient.getId(module); + FridaKernelModule pred = kmodules.get(id); + if (!kmodules.containsKey(id) || !module.equals(pred)) { + FridaModuleInfo info = new FridaModuleInfo(module); + if (!kmodules.containsKey(id)) { + getClient().processEvent(new FridaModuleLoadedEvent(info)); + } + else { + getClient().processEvent(new FridaModuleReplacedEvent(info)); + } + kmodules.put(id, module); + } + } + } + + /** + * @param process wrapper for Frida pointer + * @param id the module name to remove + */ + public void removeMemoryRegion(FridaProcess process, String id) { + synchronized (regions) { + if (regions.get(FridaClient.getId(process)).remove(id) == null) { + throw new IllegalArgumentException("There is no region with id " + id); + } + } + } + + @Override + public FridaMemoryRegionInfo getMemoryRegion(FridaProcess process, String id) { + synchronized (regions) { + return regions.get(FridaClient.getId(process)).get(id); + } + } + + public void addMemoryRegionIfAbsent(FridaProcess process, FridaMemoryRegionInfo region) { + synchronized (regions) { + String sessionId = FridaClient.getId(process); + Map map = regions.get(sessionId); + if (map == null) { + map = new HashMap<>(); + regions.put(sessionId, map); + } + String id = FridaClient.getId(region); + FridaMemoryRegionInfo pred = map.get(id); + if (!map.containsKey(id) || !region.equals(pred)) { + FridaRegionInfo info = new FridaRegionInfo(process, region); + if (!map.containsKey(id)) { + getClient().processEvent(new FridaMemoryRegionAddedEvent(info)); + } + else { + getClient().processEvent(new FridaMemoryRegionReplacedEvent(info)); + } + map.put(id, region); + } + } + } + + public void addKernelMemoryRegionIfAbsent(FridaKernelMemoryRegionInfo region) { + synchronized (kregions) { + String id = FridaClient.getId(region); + FridaKernelMemoryRegionInfo pred = kregions.get(id); + if (!kregions.containsKey(id) || !region.equals(pred)) { + FridaRegionInfo info = new FridaRegionInfo(region); + if (!kregions.containsKey(id)) { + getClient().processEvent(new FridaMemoryRegionAddedEvent(info)); + } + else { + getClient().processEvent(new FridaMemoryRegionReplacedEvent(info)); + } + kregions.put(id, region); + } + } + } + + @Override + public Map getKnownThreads(FridaProcess process) { + String processId = FridaClient.getId(process); + Map map = threads.get(processId); + if (map == null) { + map = new HashMap<>(); + threads.put(FridaClient.getId(process), map); + } + return map; + } + + @Override + public Map getKnownProcesses(FridaSession target) { + String sessionId = FridaClient.getId(target); + Map map = processes.get(sessionId); + if (map == null) { + map = new HashMap<>(); + processes.put(sessionId, map); + } + return map; + } + + @Override + public Map getKnownSessions() { + return unmodifiableSessions; + } + + @Override + public Map getKnownModules(FridaProcess process) { + String processId = FridaClient.getId(process); + Map map = modules.get(processId); + if (map == null) { + map = new HashMap<>(); + modules.put(processId, map); + } + return map; + } + + @Override + public Map getKnownRegions(FridaProcess process) { + String processId = FridaClient.getId(process); + Map map = regions.get(processId); + if (map == null) { + map = new HashMap<>(); + regions.put(processId, map); + } + return map; + } + + @Override + public CompletableFuture start(String[] args) { + state.set(null, Causes.UNCLAIMED); + boolean create = true; + if (args.length == 0) { + executor = + new FridaClientThreadExecutor(() -> FridaClient.debugCreate().createClient()); + } + else { + // TODO - process args? + executor = + new FridaClientThreadExecutor(() -> FridaClient.debugCreate().createClient()); + create = false; + } + executor.setManager(this); + AtomicReference creat = new AtomicReference<>(create); + return sequence(TypeSpec.VOID).then(executor, (seq) -> { + doExecute(creat.get()); + seq.exit(); + }).finish().exceptionally((exc) -> { + Msg.error(this, "start failed"); + return null; + }); + } + + protected void doExecute(Boolean create) { + FridaClient client = executor.getClient(); + reentrantClient = client; + + status = client.getExecutionStatus(); + } + + @Override + public boolean isRunning() { + return !executor.isShutdown() && !executor.isTerminated(); + } + + @Override + public void terminate() { + executor.execute(100, client -> { + Msg.debug(this, "Disconnecting DebugClient from session"); + client.endSession(getCurrentTarget(), DebugEndSessionFlags.DEBUG_END_DISCONNECT); + //client.setOutputCallbacks(null); + }); + executor.shutdown(); + try { + executor.awaitTermination(5000, TimeUnit.MILLISECONDS); + } + catch (InterruptedException e) { + // Eh, just go on + } + } + + @Override + public void close() throws Exception { + terminate(); + } + + /** + * Schedule a command for execution + * + * @param cmd the command to execute + * @return the pending command, which acts as a future for later completion + */ + //@Override + @Override + public CompletableFuture execute(FridaCommand cmd) { + assert cmd != null; + FridaPendingCommand pcmd = new FridaPendingCommand<>(cmd); + + if (executor.isCurrentThread()) { + try { + addCommand(cmd, pcmd); + } + catch (Throwable exc) { + pcmd.completeExceptionally(exc); + } + } + else { + CompletableFuture.runAsync(() -> { + addCommand(cmd, pcmd); + }, executor).exceptionally((exc) -> { + pcmd.completeExceptionally(exc); + return null; + }); + } + return pcmd; + } + + private void addCommand(FridaCommand cmd, FridaPendingCommand pcmd) { + synchronized (this) { + if (!cmd.validInState(state.get())) { + throw new FridaCommandError("Command " + cmd + " is not valid while " + state.get()); + } + activeCmds.add(pcmd); + } + cmd.invoke(); + processEvent(new FridaCommandDoneEvent(cmd)); + } + + @Override + public DebugStatus processEvent(FridaEvent evt) { + if (state == null) { + state.set(FridaState.FRIDA_THREAD_STOPPED, Causes.UNCLAIMED); + } + FridaState newState = evt.newState(); + if (newState != null && !(evt instanceof FridaCommandDoneEvent)) { + Msg.debug(this, evt + " transitions state to " + newState); + state.set(newState, evt.getCause()); + } + + boolean cmdFinished = false; + List> toRemove = new ArrayList>(); + for (FridaPendingCommand pcmd : activeCmds) { + cmdFinished = pcmd.handle(evt); + if (cmdFinished) { + pcmd.finish(); + toRemove.add(pcmd); + } + } + for (FridaPendingCommand pcmd : toRemove) { + activeCmds.remove(pcmd); + } + + synchronized (this) { + boolean waitState = isWaiting(); + waiting = false; + DebugStatus ret = evt.isStolen() ? null : handlerMap.handle(evt, null); + if (ret == null) { + ret = DebugStatus.NO_CHANGE; + } + waiting = ret.equals(DebugStatus.NO_DEBUGGEE) ? false : waitState; + return ret; + } + } + + @Override + public void addStateListener(FridaStateListener listener) { + state.addChangeListener(listener); + } + + @Override + public void removeStateListener(FridaStateListener listener) { + state.removeChangeListener(listener); + } + + public ListenerSet getEventListeners() { + return listenersEvent; + } + + @Override + public void addEventsListener(FridaEventsListener listener) { + getEventListeners().add(listener); + } + + @Override + public void removeEventsListener(FridaEventsListener listener) { + getEventListeners().remove(listener); + } + + private void defaultHandlers() { + handlerMap.put(FridaInterruptEvent.class, this::processInterrupt); + handlerMap.put(FridaThreadCreatedEvent.class, this::processThreadCreated); + handlerMap.put(FridaThreadReplacedEvent.class, this::processThreadReplaced); + handlerMap.put(FridaThreadExitedEvent.class, this::processThreadExited); + handlerMap.put(FridaThreadSelectedEvent.class, this::processThreadSelected); + handlerMap.put(FridaProcessCreatedEvent.class, this::processProcessCreated); + handlerMap.put(FridaProcessReplacedEvent.class, this::processProcessReplaced); + handlerMap.put(FridaProcessExitedEvent.class, this::processProcessExited); + handlerMap.put(FridaSessionCreatedEvent.class, this::processSessionCreated); + handlerMap.put(FridaSessionReplacedEvent.class, this::processSessionReplaced); + handlerMap.put(FridaSessionExitedEvent.class, this::processSessionExited); + handlerMap.put(FridaProcessSelectedEvent.class, this::processProcessSelected); + handlerMap.put(FridaSelectedFrameChangedEvent.class, this::processFrameSelected); + handlerMap.put(FridaModuleLoadedEvent.class, this::processModuleLoaded); + handlerMap.put(FridaModuleReplacedEvent.class, this::processModuleReplaced); + handlerMap.put(FridaModuleUnloadedEvent.class, this::processModuleUnloaded); + handlerMap.put(FridaMemoryRegionAddedEvent.class, this::processMemoryRegionAdded); + handlerMap.put(FridaMemoryRegionReplacedEvent.class, this::processMemoryRegionReplaced); + handlerMap.put(FridaModuleUnloadedEvent.class, this::processModuleUnloaded); + handlerMap.put(FridaStateChangedEvent.class, this::processStateChanged); + handlerMap.putVoid(FridaCommandDoneEvent.class, this::processDefault); + handlerMap.putVoid(FridaStoppedEvent.class, this::processDefault); + handlerMap.putVoid(FridaRunningEvent.class, this::processDefault); + handlerMap.putVoid(FridaConsoleOutputEvent.class, this::processConsoleOutput); + + statusMap.put(FridaProcessCreatedEvent.class, DebugStatus.BREAK); + statusMap.put(FridaProcessExitedEvent.class, DebugStatus.NO_DEBUGGEE); + statusMap.put(FridaStateChangedEvent.class, DebugStatus.NO_CHANGE); + statusMap.put(FridaStoppedEvent.class, DebugStatus.BREAK); + statusMap.put(FridaInterruptEvent.class, DebugStatus.BREAK); + } + + @Override + public void updateState(FridaSession session) { + getSessionAttributes(session); + currentSession = session; + FridaProcess process = session.getProcess(); + currentProcess = process; + if (currentSession == null || + !currentSession.equals(process.getSession())) { + FridaSession candidateSession = currentProcess.getSession(); + if (candidateSession != null) { + currentSession = candidateSession; + } + } + /* + if (currentThread == null || + !currentThread.equals(process.GetSelectedThread())) { + FridaThread candidateThread = currentProcess.GetSelectedThread(); + if (candidateThread != null) { + currentThread = candidateThread; + } + } + */ + addSessionIfAbsent(currentSession); + addProcessIfAbsent(currentSession, currentProcess); + addThreadIfAbsent(currentProcess, currentThread); + } + + /** + * Default handler for events + * + * @param evt the event + * @param v nothing + * @return retval handling/break status + */ + protected DebugStatus processDefault(AbstractFridaEvent evt, Void v) { + return statusMap.get(evt.getClass()); + } + + /** + * Handler for breakpoint events + * + * @param evt the event + * @param v nothing + * @return retval handling/break status + */ + protected DebugStatus processInterrupt(FridaInterruptEvent evt, Void v) { + return statusMap.get(evt.getClass()); + } + + /** + * Handler for thread created events + * + * @param evt the event + * @param v nothing + * @return retval handling/break status + */ + protected DebugStatus processThreadCreated(FridaThreadCreatedEvent evt, Void v) { + FridaThread thread = evt.getInfo().thread; + currentThread = thread; + getEventListeners().fire.threadCreated(thread, FridaCause.Causes.UNCLAIMED); + getEventListeners().fire.threadSelected(thread, null, evt.getCause()); + return statusMap.get(evt.getClass()); + } + + /** + * Handler for thread created events + * + * @param evt the event + * @param v nothing + * @return retval handling/break status + */ + protected DebugStatus processThreadReplaced(FridaThreadReplacedEvent evt, Void v) { + FridaThread thread = evt.getInfo().thread; + getEventListeners().fire.threadReplaced(thread, FridaCause.Causes.UNCLAIMED); + getEventListeners().fire.threadSelected(thread, null, evt.getCause()); + return statusMap.get(evt.getClass()); + } + + /** + * Handler for thread exited events + * + * @param evt the event + * @param v nothing + * @return retval handling/break status + */ + protected DebugStatus processThreadExited(FridaThreadExitedEvent evt, Void v) { + getEventListeners().fire.threadExited(currentThread, currentProcess, evt.getCause()); + return statusMap.get(evt.getClass()); + } + + /** + * Handler for thread selected events + * + * @param evt the event + * @param v nothing + * @return retval handling/break status + */ + protected DebugStatus processThreadSelected(FridaThreadSelectedEvent evt, Void v) { + currentThread = evt.getThread(); + getEventListeners().fire.threadSelected(currentThread, evt.getFrame(), evt.getCause()); + return statusMap.get(evt.getClass()); + } + + /** + * Handler for frame selected events + * + * @param evt the event + * @param v nothing + * @return retval handling/break status + */ + protected DebugStatus processFrameSelected(FridaSelectedFrameChangedEvent evt, Void v) { + currentThread = evt.getThread(); + getEventListeners().fire.threadSelected(currentThread, evt.getFrame(), evt.getCause()); + return statusMap.get(evt.getClass()); + } + + /** + * Handler for process created events + * + * @param evt the event + * @param v nothing + * @return retval handling/break status + */ + protected DebugStatus processProcessCreated(FridaProcessCreatedEvent evt, Void v) { + FridaProcessInfo info = evt.getInfo(); + FridaProcess proc = info.process; + getEventListeners().fire.processAdded(proc, FridaCause.Causes.UNCLAIMED); + getEventListeners().fire.processSelected(proc, evt.getCause()); + + /* + FridaThread thread = proc.GetSelectedThread(); + getEventListeners().fire.threadSelected(thread, null, evt.getCause()); + */ + return statusMap.get(evt.getClass()); + } + + /** + * Handler for process replaced events + * + * @param evt the event + * @param v nothing + * @return retval handling/break status + */ + protected DebugStatus processProcessReplaced(FridaProcessReplacedEvent evt, Void v) { + FridaProcessInfo info = evt.getInfo(); + FridaProcess proc = info.process; + getEventListeners().fire.processReplaced(proc, FridaCause.Causes.UNCLAIMED); + getEventListeners().fire.processSelected(proc, evt.getCause()); + + /* + FridaThread thread = proc.GetSelectedThread(); + getEventListeners().fire.threadSelected(thread, null, evt.getCause()); + */ + return statusMap.get(evt.getClass()); + } + + /** + * Handler for process exited events + * + * @param evt the event + * @param v nothing + * @return retval handling/break status + */ + protected DebugStatus processProcessExited(FridaProcessExitedEvent evt, Void v) { + FridaThread thread = getCurrentThread(); + FridaProcess process = getCurrentProcess(); + getEventListeners().fire.threadExited(thread, process, evt.getCause()); + getEventListeners().fire.processExited(process, evt.getCause()); + getEventListeners().fire.processRemoved(process.getPID().toString(), evt.getCause()); + return statusMap.get(evt.getClass()); + } + + /** + * Handler for process selected events + * + * @param evt the event + * @param v nothing + * @return retval handling/break status + */ + protected DebugStatus processProcessSelected(FridaProcessSelectedEvent evt, Void v) { + currentProcess = evt.getProcess(); + getEventListeners().fire.processSelected(currentProcess, evt.getCause()); + return statusMap.get(evt.getClass()); + } + + /** + * Handler for session created events + * + * @param evt the event + * @param v nothing + * @return retval handling/break status + */ + protected DebugStatus processSessionCreated(FridaSessionCreatedEvent evt, Void v) { + FridaSessionInfo info = evt.getInfo(); + getEventListeners().fire.sessionAdded(info.session, FridaCause.Causes.UNCLAIMED); + getEventListeners().fire.sessionSelected(info.session, evt.getCause()); + return statusMap.get(evt.getClass()); + } + + /** + * Handler for session replaced events + * + * @param evt the event + * @param v nothing + * @return retval handling/break status + */ + protected DebugStatus processSessionReplaced(FridaSessionReplacedEvent evt, Void v) { + FridaSessionInfo info = evt.getInfo(); + getEventListeners().fire.sessionReplaced(info.session, FridaCause.Causes.UNCLAIMED); + getEventListeners().fire.sessionSelected(info.session, evt.getCause()); + return statusMap.get(evt.getClass()); + } + + /** + * Handler for session exited events + * + * @param evt the event + * @param v nothing + * @return retval handling/break status + */ + protected DebugStatus processSessionExited(FridaSessionExitedEvent evt, Void v) { + removeSession(evt.sessionId, FridaCause.Causes.UNCLAIMED); + getEventListeners().fire.sessionRemoved(evt.sessionId, evt.getCause()); + getEventListeners().fire.threadExited(currentThread, currentProcess, evt.getCause()); + getEventListeners().fire.processExited(currentProcess, evt.getCause()); + getEventListeners().fire.processRemoved(currentProcess.getPID().toString(), + evt.getCause()); + return statusMap.get(evt.getClass()); + } + + /** + * Handler for module loaded events + * + * @param evt the event + * @param v nothing + * @return retval handling/break status + */ + protected DebugStatus processModuleLoaded(FridaModuleLoadedEvent evt, Void v) { + FridaModuleInfo info = evt.getInfo(); + long n = info.getNumberOfModules(); + FridaProcess process = info.getProcess(); + for (int i = 0; i < n; i++) { + getEventListeners().fire.moduleLoaded(process, info, i, evt.getCause()); + } + return statusMap.get(evt.getClass()); + } + + /** + * Handler for module replaced events + * + * @param evt the event + * @param v nothing + * @return retval handling/break status + */ + protected DebugStatus processModuleReplaced(FridaModuleReplacedEvent evt, Void v) { + FridaModuleInfo info = evt.getInfo(); + long n = info.getNumberOfModules(); + FridaProcess process = info.getProcess(); + for (int i = 0; i < n; i++) { + getEventListeners().fire.moduleReplaced(process, info, i, evt.getCause()); + } + return statusMap.get(evt.getClass()); + } + + /** + * Handler for module unloaded events + * + * @param evt the event + * @param v nothing + * @return retval handling/break status + */ + protected DebugStatus processModuleUnloaded(FridaModuleUnloadedEvent evt, Void v) { + FridaModuleInfo info = evt.getInfo(); + long n = info.getNumberOfModules(); + FridaProcess process = info.getProcess(); + for (int i = 0; i < n; i++) { + getEventListeners().fire.moduleUnloaded(process, info, i, evt.getCause()); + } + return statusMap.get(evt.getClass()); + } + + /** + * Handler for module loaded events + * + * @param evt the event + * @param v nothing + * @return retval handling/break status + */ + protected DebugStatus processMemoryRegionAdded(FridaMemoryRegionAddedEvent evt, Void v) { + FridaRegionInfo info = evt.getInfo(); + long n = info.getNumberOfRegions(); + FridaProcess process = info.getProcess(); + for (int i = 0; i < n; i++) { + getEventListeners().fire.regionAdded(process, info, i, evt.getCause()); + } + return statusMap.get(evt.getClass()); + } + + /** + * Handler for module replaced events + * + * @param evt the event + * @param v nothing + * @return retval handling/break status + */ + protected DebugStatus processMemoryRegionReplaced(FridaMemoryRegionReplacedEvent evt, Void v) { + FridaRegionInfo info = evt.getInfo(); + long n = info.getNumberOfRegions(); + FridaProcess process = info.getProcess(); + for (int i = 0; i < n; i++) { + getEventListeners().fire.regionReplaced(process, info, i, evt.getCause()); + } + return statusMap.get(evt.getClass()); + } + + /** + * Handler for module unloaded events + * + * @param evt the event + * @param v nothing + * @return retval handling/break status + */ + protected DebugStatus processMemoryRegionRemoved(FridaMemoryRegionRemovedEvent evt, Void v) { + FridaRegionInfo info = evt.getInfo(); + long n = info.getNumberOfRegions(); + FridaProcess process = info.getProcess(); + for (int i = 0; i < n; i++) { + getEventListeners().fire.regionRemoved(process, info, i, evt.getCause()); + } + return statusMap.get(evt.getClass()); + } + + /** + * Handler for state changed events + * + * @param evt the event + * @param v nothing + * @return retval handling/break status + */ + protected DebugStatus processStateChanged(FridaStateChangedEvent evt, Void v) { + status = DebugStatus.fromArgument(evt.newState()); + + String id = currentThread == null ? FridaClient.getId(currentProcess) : FridaClient.getId(currentThread); + if (status.equals(DebugStatus.NO_DEBUGGEE)) { + waiting = false; + if (state.get().equals(FridaState.FRIDA_THREAD_HALTED)) { + if (currentThread != null) { + processEvent(new FridaRunningEvent(id)); + } + processEvent(new FridaProcessExitedEvent(0)); + processEvent(new FridaSessionExitedEvent(FridaClient.getId(currentSession), 0)); + } + return DebugStatus.NO_DEBUGGEE; + } + if (status.equals(DebugStatus.BREAK)) { + waiting = false; + FridaProcess process = getCurrentProcess(); + if (process != null) { + processEvent(new FridaProcessSelectedEvent(process)); + if (currentThread != null) { + FridaThreadInfo tinfo = new FridaThreadInfo(currentThread); + processEvent(new FridaThreadSelectedEvent(tinfo)); + } + } + processEvent(new FridaStoppedEvent(id)); + return DebugStatus.BREAK; + } + if (status.equals(DebugStatus.GO)) { + waiting = true; + processEvent(new FridaRunningEvent(id)); + return DebugStatus.GO; + } + + waiting = false; + return DebugStatus.NO_CHANGE; + } + + /** + * Handler for session selected events + * + * @param evt the event + * @param v nothing + * @return retval handling/break status + */ + protected DebugStatus processSessionSelected(FridaSessionSelectedEvent evt, Void v) { + FridaSession session = evt.getSession(); + getEventListeners().fire.sessionSelected(session, evt.getCause()); + return statusMap.get(evt.getClass()); + } + + protected void processConsoleOutput(FridaConsoleOutputEvent evt, Void v) { + if (evt.getOutput() != null) { + getEventListeners().fire.consoleOutput(evt.getOutput(), evt.getMask()); + } + } + + @Override + public CompletableFuture listThreads(FridaProcess process) { + return execute(new FridaListThreadsCommand(this, process)); + } + + @Override + public CompletableFuture> listProcesses(FridaSession session) { + return execute(new FridaListProcessesCommand(this, session)); + } + + @Override + public CompletableFuture>> listAvailableProcesses() { + return execute(new FridaListAvailableProcessesCommand(this)); + } + + @Override + public CompletableFuture> listSessions() { + return execute(new FridaListSessionsCommand(this)); + } + + @Override + public CompletableFuture> listStackFrames(FridaThread thread) { + return execute(new FridaListStackFramesCommand(this, thread)); + } + + @Override + public CompletableFuture> listRegisters(FridaThread thread) { + return execute(new FridaListRegistersCommand(this, thread)); + } + + @Override + public CompletableFuture listModules(FridaProcess process) { + return execute(new FridaListModulesCommand(this, process)); + } + public CompletableFuture listKernelModules() { + return execute(new FridaListKernelModulesCommand(this)); + } + + @Override + public CompletableFuture> listModuleSections(FridaModule module) { + return execute(new FridaListModuleSectionsCommand(this, module)); + } + + @Override + public CompletableFuture> listModuleSymbols(FridaModule module) { + return execute(new FridaListModuleSymbolsCommand(this, module)); + } + + @Override + public CompletableFuture> listModuleImports(FridaModule module) { + return execute(new FridaListModuleImportsCommand(this, module)); + } + + @Override + public CompletableFuture> listModuleExports(FridaModule module) { + return execute(new FridaListModuleExportsCommand(this, module)); + } + + @Override + public CompletableFuture listMemory(FridaProcess process) { + return execute(new FridaListMemoryRegionsCommand(this, process)); + } + public CompletableFuture listKernelMemory() { + return execute(new FridaListKernelMemoryRegionsCommand(this)); + } + + @Override + public CompletableFuture listHeapMemory(FridaProcess process) { + return execute(new FridaListHeapMemoryRegionsCommand(this, process)); + } + + @Override + public CompletableFuture setExceptionHandler(FridaProcess process) { + return execute(new FridaSetExceptionHandlerCommand(this, process)); + } + + @Override + public CompletableFuture getSessionAttributes(FridaSession session) { + return execute(new FridaGetSessionAttributesCommand(this, session)); + } + + @Override + public CompletableFuture addProcess() { + return execute(new FridaAddProcessCommand(this)); + } + + @Override + public CompletableFuture removeProcess(FridaProcess process) { + return execute(new FridaRemoveProcessCommand(this, process.getSession(), + process.getPID().toString())); + } + + @Override + public CompletableFuture addSession() { + return execute(new FridaAddSessionCommand(this)); + } + + @Override + public CompletableFuture attach(String pid) { + return execute(new FridaAttachCommand(this, pid)); + } + + @Override + public CompletableFuture launch(String fileName, List args) { + return execute(new FridaLaunchProcessCommand(this, fileName, args)); + } + + @Override + public CompletableFuture launch(Map args) { + return execute(new FridaLaunchProcessWithOptionsCommand(this, args)); + } + + public FridaClient getClient() { + return executor.getClient(); + } + + public FridaThread getCurrentThread() { + return currentThread; + } + + public void setCurrentThread(FridaThread thread) { + currentThread = thread; + } + + public void setCurrentThreadById(String tid) { + currentProcess = currentSession.getProcess(); + if (currentProcess != null) { + String key = FridaClient.getId(currentProcess); + currentThread = threads.get(key).get(tid); + } + } + + public FridaProcess getCurrentProcess() { + return currentProcess; + } + + public FridaSession getCurrentSession() { + return currentSession; + } + + public void setCurrentSession(FridaSession session) { + currentSession = session; + } + + public FridaTarget getCurrentTarget() { + return currentTarget; + } + + public void setCurrentTarget(FridaTarget target) { + currentTarget = target; + } + + public CompletableFuture requestFocus(FridaModelTargetFocusScope scope, TargetObject obj) { + return execute(new FridaRequestFocusCommand(this, scope, obj)); + } + + public CompletableFuture requestActivation(FridaModelTargetActiveScope activator, + TargetObject obj) { + return execute(new FridaRequestActivationCommand(this, activator, obj)); + } + + @Override + public CompletableFuture console(String command) { + if (continuation != null) { + String prompt = command.equals("") ? FridaModelTargetInterpreter.FRIDA_PROMPT : ">>>"; + getEventListeners().fire.promptChanged(prompt); + continuation.complete(command); + setContinuation(null); + return AsyncUtils.NIL; + } + return execute( + new FridaConsoleExecCommand(this, command, FridaConsoleExecCommand.Output.CONSOLE)) + .thenApply(e -> null); + } + + @Override + public CompletableFuture consoleCapture(String command) { + return execute( + new FridaConsoleExecCommand(this, command, FridaConsoleExecCommand.Output.CAPTURE)); + } + + @Override + public FridaState getState() { + if (currentThread == null) { + return null; + } + if (currentThread != null) { + return FridaState.FRIDA_THREAD_RUNNING; + } + return currentThread.getState(); + } + + @Override + public FridaProcess currentProcess() { + return getCurrentProcess(); + } + + @Override + public CompletableFuture waitForPrompt() { + return CompletableFuture.completedFuture(null); + } + + public CompletableFuture> getRegisterMap(List path) { + return null; + } + + public boolean isWaiting() { + return waiting; + } + + public boolean isKernelMode() { + return kernelMode; + } + + public void setKernelMode(boolean kernelMode) { + this.kernelMode = kernelMode; + } + + public void setContinuation(CompletableFuture continuation) { + this.continuation = continuation; + } + + @Override + public DebugStatus getStatus() { + return status; + } + + public FridaScript loadPermanentScript(AbstractFridaCommand caller, String name, String scriptText) { + caller.setName(name); + Pointer options = FridaEng.createOptions(name); + FridaScript script = FridaEng.createScript(currentSession, scriptText, options); + NativeLong signal = FridaEng.connectSignal(script, "message", (__s, message, data, userData) -> { + caller.parse(message, data); + }, null); + script.setSignal(signal); + FridaEng.loadScript(script); + scripts.put(name, script); + return script; + } + + public void unloadPermanentScript(String name) { + FridaScript script = scripts.get(name); + NativeLong signal = script.getSignal(); + FridaEng.disconnectSignal(script, signal); + FridaEng.unloadScript(script); + FridaEng.unref(script); + } + + public FridaScript loadScript(AbstractFridaCommand caller, String name, String scriptText) { + caller.setName(name); + Pointer options = FridaEng.createOptions(name); + String wrapperText = scriptText.contains("result") ? + "var result = '';" + scriptText + + "var msg = { key: '" + name + "', value: result};" + + "send(JSON.stringify(msg));" : + "send(JSON.stringify(" + scriptText + "));"; + FridaScript script = FridaEng.createScript(currentSession, wrapperText, options); + if (script != null) { + NativeLong signal = FridaEng.connectSignal(script, "message", (__s, message, data, userData) -> { + caller.parse(message, data); + }, null); + script.setSignal(signal); + FridaEng.loadScript(script); + caller.setScript(script); + FridaEng.disconnectSignal(script, signal); + FridaEng.unloadScript(script); + FridaEng.unref(script); + } + return script; + } + + @Override + public void enableDebugger(FridaSession session, int port) { + FridaEng.enableDebugger(session, new NativeLong(port)); + } + + public CompletableFuture stalkThread(String tid, Map arguments) { + return execute(new FridaStalkThreadCommand(this, tid, arguments)); + } + + public CompletableFuture interceptFunction(String address, Map arguments) { + return execute(new FridaInterceptFunctionCommand(this, address, arguments)); + } + + public CompletableFuture watchMemory(Map arguments) { + return execute(new FridaWatchMemoryCommand(this, arguments)); + } + +} diff --git a/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/AbstractFridaModel.java b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/AbstractFridaModel.java new file mode 100644 index 0000000000..eba5138250 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/AbstractFridaModel.java @@ -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.model; + +import java.io.IOException; +import java.util.concurrent.CompletableFuture; + +import agent.frida.manager.FridaManager; +import agent.frida.model.iface2.FridaModelTargetSession; +import ghidra.dbg.agent.AbstractDebuggerObjectModel; +import ghidra.dbg.target.TargetObject; +import ghidra.program.model.address.AddressFactory; + +public abstract class AbstractFridaModel extends AbstractDebuggerObjectModel { + + public abstract FridaManager getManager(); + + public abstract CompletableFuture startFrida(String[] args); + + public abstract boolean isRunning(); + + public abstract void terminate() throws IOException; + + public abstract AddressFactory getAddressFactory(); + + public abstract FridaModelTargetSession getSession(); + + public abstract void addModelObject(Object object, TargetObject targetObject); + + public abstract TargetObject getModelObject(Object object); + +} diff --git a/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/iface1/FridaModelSelectableObject.java b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/iface1/FridaModelSelectableObject.java new file mode 100644 index 0000000000..dbcdc81bf2 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/iface1/FridaModelSelectableObject.java @@ -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.model.iface1; + +import java.util.concurrent.CompletableFuture; + +import agent.frida.model.iface2.FridaModelTargetObject; +import ghidra.dbg.target.schema.*; + +@TargetObjectSchemaInfo( + name = "SelectableObject", + elements = { + @TargetElementType(type = Void.class) + }, + attributes = { + @TargetAttributeType(type = Void.class) + }) +public interface FridaModelSelectableObject extends FridaModelTargetObject { + + public CompletableFuture setActive(); + +} diff --git a/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/iface1/FridaModelTargetAccessConditioned.java b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/iface1/FridaModelTargetAccessConditioned.java new file mode 100644 index 0000000000..df5060bb74 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/iface1/FridaModelTargetAccessConditioned.java @@ -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.model.iface1; + +import agent.frida.model.iface2.FridaModelTargetObject; +import ghidra.dbg.target.TargetAccessConditioned; + +/** + * An interface which indicates this object is capable of launching targets. + * + * The targets this launcher creates ought to appear in its successors. + * + * @param type for this + */ +public interface FridaModelTargetAccessConditioned + extends FridaModelTargetObject, TargetAccessConditioned { + + @Override + public boolean isAccessible(); + + public void setAccessible(boolean accessible); + +} diff --git a/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/iface1/FridaModelTargetActiveScope.java b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/iface1/FridaModelTargetActiveScope.java new file mode 100644 index 0000000000..da19e63e0b --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/iface1/FridaModelTargetActiveScope.java @@ -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.model.iface1; + +import java.util.concurrent.CompletableFuture; + +import agent.frida.model.iface2.FridaModelTargetObject; +import ghidra.async.AsyncUtils; +import ghidra.dbg.error.DebuggerIllegalArgumentException; +import ghidra.dbg.target.TargetActiveScope; +import ghidra.dbg.target.TargetObject; +import ghidra.dbg.util.PathUtils; + +/** + * An interface which indicates this object is capable of launching targets. + * + * The targets this launcher creates ought to appear in its successors. + * + * @param type for this + */ +public interface FridaModelTargetActiveScope extends FridaModelTargetObject, TargetActiveScope { + + // NB: requesActivation request change in active object - propagates down to manager + // (but, of course, may then cause change in state) + @Override + public default CompletableFuture requestActivation(TargetObject obj) { + return getModel().gateFuture(getManager().requestActivation(this, obj)); + } + + public default CompletableFuture doRequestActivation(TargetObject obj) { + if (getManager().isWaiting()) { + return AsyncUtils.NIL; + } + getModel().assertMine(TargetObject.class, obj); + if (!PathUtils.isAncestor(this.getPath(), obj.getPath())) { + throw new DebuggerIllegalArgumentException("Can only focus a successor of the scope"); + } + TargetObject cur = obj; + while (cur != null) { + if (cur instanceof FridaModelSelectableObject) { + FridaModelSelectableObject sel = (FridaModelSelectableObject) cur; + //System.err.println("requestActivation " + obj); + return sel.setActive(); + } + if (cur instanceof FridaModelTargetObject) { + FridaModelTargetObject def = (FridaModelTargetObject) cur; + cur = def.getParent(); + continue; + } + throw new AssertionError(); + } + return AsyncUtils.NIL; + } + +} diff --git a/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/iface1/FridaModelTargetAttachable.java b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/iface1/FridaModelTargetAttachable.java new file mode 100644 index 0000000000..55e4fb54bb --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/iface1/FridaModelTargetAttachable.java @@ -0,0 +1,32 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.frida.model.iface1; + +import agent.frida.model.iface2.FridaModelTargetObject; +import ghidra.dbg.target.TargetAttachable; + +/** + * An interface which indicates this object is capable of launching targets. + * + *

+ * The targets this launcher creates ought to appear in its successors. + * + * @param type for this + */ +public interface FridaModelTargetAttachable extends FridaModelTargetObject, TargetAttachable { + String PID_ATTRIBUTE_NAME = PREFIX_INVISIBLE + "pid"; + +} diff --git a/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/iface1/FridaModelTargetAttacher.java b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/iface1/FridaModelTargetAttacher.java new file mode 100644 index 0000000000..1a4afd9e8b --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/iface1/FridaModelTargetAttacher.java @@ -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.model.iface1; + +import java.util.concurrent.CompletableFuture; + +import agent.frida.model.iface2.FridaModelTargetObject; +import ghidra.dbg.target.TargetAttachable; +import ghidra.dbg.target.TargetAttacher; + +/** + * An interface which indicates this object is capable of launching targets. + * + *

+ * The targets this launcher creates ought to appear in its successors. + * + * @param type for this + */ +public interface FridaModelTargetAttacher extends FridaModelTargetObject, TargetAttacher { + + @Override + public CompletableFuture attach(TargetAttachable attachable); + + @Override + public CompletableFuture attach(long pid); + +} diff --git a/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/iface1/FridaModelTargetConfigurable.java b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/iface1/FridaModelTargetConfigurable.java new file mode 100644 index 0000000000..50db71db08 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/iface1/FridaModelTargetConfigurable.java @@ -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.model.iface1; + +import agent.frida.model.iface2.FridaModelTargetObject; +import ghidra.dbg.target.TargetConfigurable; + +/** + * An interface which indicates this object is configurable. + * + * @param type for this + */ +public interface FridaModelTargetConfigurable extends FridaModelTargetObject, TargetConfigurable { + +} diff --git a/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/iface1/FridaModelTargetDeletable.java b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/iface1/FridaModelTargetDeletable.java new file mode 100644 index 0000000000..c7ce002045 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/iface1/FridaModelTargetDeletable.java @@ -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.model.iface1; + +import java.util.concurrent.CompletableFuture; + +import agent.frida.model.iface2.FridaModelTargetObject; +import ghidra.dbg.target.TargetDeletable; + +/** + * An interface which indicates this object is capable of launching targets. + * + * The targets this launcher creates ought to appear in its successors. + * + * @param type for this + */ +public interface FridaModelTargetDeletable extends FridaModelTargetObject, TargetDeletable { + + @Override + public CompletableFuture delete(); + +} diff --git a/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/iface1/FridaModelTargetDetachable.java b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/iface1/FridaModelTargetDetachable.java new file mode 100644 index 0000000000..348bb38ff6 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/iface1/FridaModelTargetDetachable.java @@ -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.model.iface1; + +import java.util.concurrent.CompletableFuture; + +import agent.frida.model.iface2.FridaModelTargetObject; +import ghidra.dbg.target.TargetDetachable; + +/** + * An interface which indicates this object is capable of launching targets. + * + * The targets this launcher creates ought to appear in its successors. + * + * @param type for this + */ +public interface FridaModelTargetDetachable extends FridaModelTargetObject, TargetDetachable { + + @Override + public CompletableFuture detach(); + +} diff --git a/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/iface1/FridaModelTargetEnvironment.java b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/iface1/FridaModelTargetEnvironment.java new file mode 100644 index 0000000000..fb39fad79c --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/iface1/FridaModelTargetEnvironment.java @@ -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.model.iface1; + +import agent.frida.model.iface2.FridaModelTargetObject; +import ghidra.dbg.target.TargetEnvironment; + +public interface FridaModelTargetEnvironment extends FridaModelTargetObject, TargetEnvironment { + + @Override + public default String getArchitecture() { + return getTypedAttributeNowByName(ARCH_ATTRIBUTE_NAME, String.class, ""); + } + + @Override + public default String getDebugger() { + return getTypedAttributeNowByName(DEBUGGER_ATTRIBUTE_NAME, String.class, ""); + } + + @Override + public default String getOperatingSystem() { + return getTypedAttributeNowByName(OS_ATTRIBUTE_NAME, String.class, ""); + } + +} diff --git a/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/iface1/FridaModelTargetEventScope.java b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/iface1/FridaModelTargetEventScope.java new file mode 100644 index 0000000000..6f9c19f93b --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/iface1/FridaModelTargetEventScope.java @@ -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.model.iface1; + +import agent.frida.model.iface2.FridaModelTargetObject; +import ghidra.dbg.target.TargetEventScope; + +/** + * The object can emit events affecting itself and its successors + * + * @param type for this + */ +public interface FridaModelTargetEventScope extends FridaModelTargetObject, TargetEventScope { + +} diff --git a/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/iface1/FridaModelTargetExecutionStateful.java b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/iface1/FridaModelTargetExecutionStateful.java new file mode 100644 index 0000000000..8a06622f5c --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/iface1/FridaModelTargetExecutionStateful.java @@ -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.model.iface1; + +import java.util.List; +import java.util.Map; + +import agent.frida.model.iface2.FridaModelTargetObject; +import ghidra.dbg.target.TargetExecutionStateful; + +/** + * An interface which indicates this object is capable of launching targets. + * + * The targets this launcher creates ought to appear in its successors. + * + * @param type for this + */ +public interface FridaModelTargetExecutionStateful + extends FridaModelTargetObject, TargetExecutionStateful { + + public default void setExecutionState(TargetExecutionState state, String reason) { + changeAttributes(List.of(), Map.of( // + STATE_ATTRIBUTE_NAME, state // + ), reason); + if (this instanceof FridaModelTargetAccessConditioned) { + FridaModelTargetAccessConditioned conditioned = (FridaModelTargetAccessConditioned) this; + conditioned.setAccessible(!state.isRunning()); + } + } + +} diff --git a/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/iface1/FridaModelTargetFocusScope.java b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/iface1/FridaModelTargetFocusScope.java new file mode 100644 index 0000000000..2edb310b96 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/iface1/FridaModelTargetFocusScope.java @@ -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.model.iface1; + +import java.util.concurrent.CompletableFuture; + +import agent.frida.model.iface2.FridaModelTargetObject; +import ghidra.async.AsyncUtils; +import ghidra.dbg.error.DebuggerIllegalArgumentException; +import ghidra.dbg.target.TargetFocusScope; +import ghidra.dbg.target.TargetObject; +import ghidra.dbg.util.PathUtils; + +/** + * An interface which indicates this object is capable of launching targets. + * + * The targets this launcher creates ought to appear in its successors. + * + * @param type for this + */ +public interface FridaModelTargetFocusScope extends FridaModelTargetObject, TargetFocusScope { + + @Override + public FridaModelSelectableObject getFocus(); + + // NB: setFocus changes attributes - propagates up to client + public boolean setFocus(FridaModelSelectableObject sel); + + // NB: requestFocus request change in focused object - propagates down to manager + // (but, of course, may then cause change in state) + @Override + public default CompletableFuture requestFocus(TargetObject obj) { + return getModel().gateFuture(getManager().requestFocus(this, obj)); + } + + public default CompletableFuture doRequestFocus(TargetObject obj) { + if (getManager().isWaiting()) { + return AsyncUtils.NIL; + } + getModel().assertMine(TargetObject.class, obj); + if (obj.equals(getFocus())) { + return AsyncUtils.NIL; + } + if (!PathUtils.isAncestor(this.getPath(), obj.getPath())) { + throw new DebuggerIllegalArgumentException("Can only focus a successor of the scope"); + } + TargetObject cur = obj; + while (cur != null) { + if (cur instanceof FridaModelSelectableObject) { + FridaModelSelectableObject sel = (FridaModelSelectableObject) cur; + //System.err.println("requestFocus " + obj); + setFocus(sel); + return AsyncUtils.NIL; + } + if (cur instanceof FridaModelTargetObject) { + FridaModelTargetObject def = (FridaModelTargetObject) cur; + cur = def.getParent(); + continue; + } + throw new AssertionError(); + } + return AsyncUtils.NIL; + } + +} diff --git a/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/iface1/FridaModelTargetInterpreter.java b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/iface1/FridaModelTargetInterpreter.java new file mode 100644 index 0000000000..9aecaeb572 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/iface1/FridaModelTargetInterpreter.java @@ -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.model.iface1; + +import java.util.concurrent.CompletableFuture; + +import agent.frida.model.iface2.FridaModelTargetObject; +import ghidra.dbg.target.TargetInterpreter; + +/** + * An interface which indicates this object is capable of launching targets. + * + * The targets this launcher creates ought to appear in its successors. + * + * @param type for this + */ +public interface FridaModelTargetInterpreter extends FridaModelTargetObject, TargetInterpreter { + + public static final String FRIDA_PROMPT = "(frida)"; + + @Override + public default CompletableFuture execute(String cmd) { + return getModel().gateFuture(getManager().console(cmd)); + } + + @Override + public default CompletableFuture executeCapture(String cmd) { + return getModel().gateFuture(getManager().consoleCapture(cmd)); + } + +} diff --git a/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/iface1/FridaModelTargetKillable.java b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/iface1/FridaModelTargetKillable.java new file mode 100644 index 0000000000..854415fec0 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/iface1/FridaModelTargetKillable.java @@ -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.model.iface1; + +import java.util.concurrent.CompletableFuture; + +import agent.frida.model.iface2.FridaModelTargetObject; +import ghidra.dbg.target.TargetKillable; + +/** + * An interface which indicates this object is capable of launching targets. + * + * The targets this launcher creates ought to appear in its successors. + * + * @param type for this + */ +public interface FridaModelTargetKillable extends FridaModelTargetObject, TargetKillable { + + public CompletableFuture destroy(); + + @Override + public CompletableFuture kill(); + +} diff --git a/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/iface1/FridaModelTargetLauncher.java b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/iface1/FridaModelTargetLauncher.java new file mode 100644 index 0000000000..7f9df24d5e --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/iface1/FridaModelTargetLauncher.java @@ -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.model.iface1; + +import java.util.List; +import java.util.concurrent.CompletableFuture; + +import agent.frida.model.iface2.FridaModelTargetObject; +import ghidra.dbg.target.TargetLauncher.TargetCmdLineLauncher; + +/** + * An interface which indicates this object is capable of launching targets. + * + *

+ * The targets this launcher creates ought to appear in its successors. + * + * @param type for this + */ +public interface FridaModelTargetLauncher extends FridaModelTargetObject, TargetCmdLineLauncher { + + @Override + public CompletableFuture launch(List args); +} diff --git a/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/iface1/FridaModelTargetMethod.java b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/iface1/FridaModelTargetMethod.java new file mode 100644 index 0000000000..1804f21373 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/iface1/FridaModelTargetMethod.java @@ -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.model.iface1; + +import agent.frida.model.iface2.FridaModelTargetObject; +import ghidra.dbg.target.TargetMethod; + +/** + * An interface which indicates this object is a method in its parent. + * + * @param type for this + */ +public interface FridaModelTargetMethod extends FridaModelTargetObject, TargetMethod { + +} diff --git a/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/iface1/FridaModelTargetResumable.java b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/iface1/FridaModelTargetResumable.java new file mode 100644 index 0000000000..280c6b6534 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/iface1/FridaModelTargetResumable.java @@ -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.model.iface1; + +import java.util.concurrent.CompletableFuture; + +import agent.frida.model.iface2.FridaModelTargetObject; +import ghidra.dbg.target.TargetResumable; + +/** + * An interface which indicates this object is capable of launching targets. + * + * The targets this launcher creates ought to appear in its successors. + * + * @param type for this + */ +public interface FridaModelTargetResumable extends FridaModelTargetObject, TargetResumable { + + @Override + public CompletableFuture resume(); + +} diff --git a/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/iface1/FridaModelTargetSteppable.java b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/iface1/FridaModelTargetSteppable.java new file mode 100644 index 0000000000..58100d05dd --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/iface1/FridaModelTargetSteppable.java @@ -0,0 +1,30 @@ +/* ### + * 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.model.iface1; + +import agent.frida.model.iface2.FridaModelTargetObject; +import ghidra.dbg.target.TargetSteppable; + +/** + * An interface which indicates this object is capable of launching targets. + * + * The targets this launcher creates ought to appear in its successors. + * + * @param type for this + */ +public interface FridaModelTargetSteppable extends FridaModelTargetObject, TargetSteppable { + +} diff --git a/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/iface2/FridaModelTargetAvailable.java b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/iface2/FridaModelTargetAvailable.java new file mode 100644 index 0000000000..e8a61c5cab --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/iface2/FridaModelTargetAvailable.java @@ -0,0 +1,27 @@ +/* ### + * 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.model.iface2; + +import ghidra.dbg.target.TargetAttachable; + +public interface FridaModelTargetAvailable extends FridaModelTargetObject, TargetAttachable { + String PID_ATTRIBUTE_NAME = PREFIX_INVISIBLE + "pid"; + + public Long getPid(); + + public void setBase(Object value); + +} diff --git a/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/iface2/FridaModelTargetAvailableContainer.java b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/iface2/FridaModelTargetAvailableContainer.java new file mode 100644 index 0000000000..f38ab3e2a2 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/iface2/FridaModelTargetAvailableContainer.java @@ -0,0 +1,22 @@ +/* ### + * 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.model.iface2; + +public interface FridaModelTargetAvailableContainer extends FridaModelTargetObject { + + public FridaModelTargetAvailable getTargetAttachable(String pid); + +} diff --git a/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/iface2/FridaModelTargetConnector.java b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/iface2/FridaModelTargetConnector.java new file mode 100644 index 0000000000..bb0d8ff05d --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/iface2/FridaModelTargetConnector.java @@ -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.model.iface2; + +import java.util.Map; +import java.util.concurrent.CompletableFuture; + +import agent.frida.model.iface1.FridaModelSelectableObject; +import ghidra.dbg.target.TargetLauncher; +import ghidra.dbg.target.TargetMethod.TargetParameterMap; + +public interface FridaModelTargetConnector + extends FridaModelSelectableObject, TargetLauncher { + + @Override + public default String getDisplay() { + return getName(); + } + + @Override + public CompletableFuture setActive(); + + @Override + public TargetParameterMap getParameters(); + + @Override + public CompletableFuture launch(Map args); +} diff --git a/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/iface2/FridaModelTargetConnectorContainer.java b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/iface2/FridaModelTargetConnectorContainer.java new file mode 100644 index 0000000000..aced1042de --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/iface2/FridaModelTargetConnectorContainer.java @@ -0,0 +1,22 @@ +/* ### + * 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.model.iface2; + +public interface FridaModelTargetConnectorContainer extends FridaModelTargetObject { + + public FridaModelTargetAvailable getTargetAttachable(int pid); + +} diff --git a/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/iface2/FridaModelTargetExport.java b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/iface2/FridaModelTargetExport.java new file mode 100644 index 0000000000..6937ef65c9 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/iface2/FridaModelTargetExport.java @@ -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.model.iface2; + +import ghidra.program.model.address.Address; + +public interface FridaModelTargetExport extends FridaModelTargetObject { + + @Override + public String getName(); + + @Override + public Address getValue(); + +} diff --git a/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/iface2/FridaModelTargetExportContainer.java b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/iface2/FridaModelTargetExportContainer.java new file mode 100644 index 0000000000..3e98627195 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/iface2/FridaModelTargetExportContainer.java @@ -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.model.iface2; + +import agent.frida.manager.FridaExport; +import agent.frida.model.impl.FridaModelTargetExportImpl; + +public interface FridaModelTargetExportContainer + extends FridaModelTargetObject { + + public FridaModelTargetExportImpl getTargetExport(FridaExport exp); + +} diff --git a/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/iface2/FridaModelTargetFileSpec.java b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/iface2/FridaModelTargetFileSpec.java new file mode 100644 index 0000000000..ca7e4d1f1d --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/iface2/FridaModelTargetFileSpec.java @@ -0,0 +1,22 @@ +/* ### + * 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.model.iface2; + +import ghidra.dbg.target.TargetObject; + +public interface FridaModelTargetFileSpec extends FridaModelTargetObject, TargetObject { + +} diff --git a/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/iface2/FridaModelTargetFunction.java b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/iface2/FridaModelTargetFunction.java new file mode 100644 index 0000000000..90f10aac75 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/iface2/FridaModelTargetFunction.java @@ -0,0 +1,20 @@ +/* ### + * 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.model.iface2; + +public interface FridaModelTargetFunction extends FridaModelTargetObject { + +} diff --git a/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/iface2/FridaModelTargetImport.java b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/iface2/FridaModelTargetImport.java new file mode 100644 index 0000000000..86d2011101 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/iface2/FridaModelTargetImport.java @@ -0,0 +1,29 @@ +/* ### + * 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.model.iface2; + +import ghidra.program.model.address.Address; + +public interface FridaModelTargetImport extends FridaModelTargetObject { + + @Override + public String getName(); + + @Override + public Address getValue(); + + public long getSize(); +} diff --git a/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/iface2/FridaModelTargetImportContainer.java b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/iface2/FridaModelTargetImportContainer.java new file mode 100644 index 0000000000..1bb498df44 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/iface2/FridaModelTargetImportContainer.java @@ -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.model.iface2; + +import agent.frida.manager.FridaImport; +import agent.frida.model.impl.FridaModelTargetImportImpl; + +public interface FridaModelTargetImportContainer + extends FridaModelTargetObject { + + public FridaModelTargetImportImpl getTargetImport(FridaImport imp); + +} diff --git a/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/iface2/FridaModelTargetMemoryContainer.java b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/iface2/FridaModelTargetMemoryContainer.java new file mode 100644 index 0000000000..8932453d98 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/iface2/FridaModelTargetMemoryContainer.java @@ -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.model.iface2; + +import java.util.concurrent.CompletableFuture; + +import agent.frida.frida.FridaRegionInfo; +import agent.frida.manager.*; +import ghidra.dbg.target.TargetMemory; +import ghidra.program.model.address.Address; + +public interface FridaModelTargetMemoryContainer extends FridaModelTargetObject, TargetMemory, // + FridaEventsListenerAdapter { + + public FridaModelTargetMemoryRegion getTargetMemory(FridaMemoryRegionInfo region); + + public void regionAdded(FridaProcess process, FridaRegionInfo info, int index, FridaCause cause); + + public void regionReplaced(FridaProcess process, FridaRegionInfo info, int index, FridaCause cause); + + public void regionRemoved(FridaProcess process, FridaRegionInfo info, int index, FridaCause cause); + + @Override + public CompletableFuture readMemory(Address address, int length); + + @Override + public CompletableFuture writeMemory(Address address, byte[] data); + +} diff --git a/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/iface2/FridaModelTargetMemoryRegion.java b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/iface2/FridaModelTargetMemoryRegion.java new file mode 100644 index 0000000000..655208b7cc --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/iface2/FridaModelTargetMemoryRegion.java @@ -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.model.iface2; + +import ghidra.dbg.target.TargetMemoryRegion; +import ghidra.program.model.address.AddressRange; + +public interface FridaModelTargetMemoryRegion extends FridaModelTargetObject, TargetMemoryRegion { + + @Override + public AddressRange getRange(); + + @Override + public boolean isReadable(); + + @Override + public boolean isWritable(); + + @Override + public boolean isExecutable(); + +} diff --git a/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/iface2/FridaModelTargetModule.java b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/iface2/FridaModelTargetModule.java new file mode 100644 index 0000000000..bfa0dc9202 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/iface2/FridaModelTargetModule.java @@ -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.model.iface2; + +import java.util.Map; +import java.util.concurrent.CompletableFuture; + +import agent.frida.manager.FridaModule; +import ghidra.dbg.target.TargetModule; +import ghidra.dbg.target.TargetObject; +import ghidra.program.model.address.Address; +import ghidra.program.model.address.AddressRange; +import ghidra.program.model.address.AddressRangeImpl; +import ghidra.program.model.address.AddressSpace; + +public interface FridaModelTargetModule extends FridaModelTargetObject, TargetModule { + + FridaModule getModule(); + + @Override + public default CompletableFuture init(Map map) { + AddressSpace space = getModel().getAddressSpace("ram"); + return requestNativeAttributes().thenAccept(attrs -> { + if (attrs != null) { + map.putAll(attrs); + TargetObject baseOffset2 = (TargetObject) attrs.get("BaseAddress"); + TargetObject nameAttr = (TargetObject) attrs.get("Name"); + TargetObject size = (TargetObject) attrs.get("Size"); + String basestr = baseOffset2 == null ? "0" + : baseOffset2.getCachedAttribute(VALUE_ATTRIBUTE_NAME).toString(); + String namestr = nameAttr == null ? "" + : nameAttr.getCachedAttribute(VALUE_ATTRIBUTE_NAME).toString(); + String sizestr = + size == null ? "1" : size.getCachedAttribute(VALUE_ATTRIBUTE_NAME).toString(); + String shortnamestr = namestr; + int sep = shortnamestr.lastIndexOf('\\'); + if (sep > 0 && sep < shortnamestr.length()) { + shortnamestr = shortnamestr.substring(sep + 1); + } + Long base = Long.parseUnsignedLong(basestr, 16); + Integer sz = Integer.parseInt(sizestr, 16); + Address min = space.getAddress(base); + Address max = min.add(sz - 1); + AddressRange range = new AddressRangeImpl(min, max); + if (range != null) { + map.put(RANGE_ATTRIBUTE_NAME, range); + } + + String oldval = (String) getCachedAttribute(DISPLAY_ATTRIBUTE_NAME); + map.put(MODULE_NAME_ATTRIBUTE_NAME, namestr); + map.put(SHORT_DISPLAY_ATTRIBUTE_NAME, shortnamestr); + map.put(DISPLAY_ATTRIBUTE_NAME, shortnamestr); + setModified(map, !shortnamestr.equals(oldval)); + } + }); + } + + void setRange(AddressRangeImpl addressRangeImpl); + +} diff --git a/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/iface2/FridaModelTargetModuleContainer.java b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/iface2/FridaModelTargetModuleContainer.java new file mode 100644 index 0000000000..94111b3026 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/iface2/FridaModelTargetModuleContainer.java @@ -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.model.iface2; + +import java.util.concurrent.CompletableFuture; + +import agent.frida.frida.FridaModuleInfo; +import agent.frida.manager.*; +import agent.frida.model.iface1.FridaModelTargetEventScope; +import ghidra.dbg.target.TargetModule; +import ghidra.dbg.target.TargetModuleContainer; + +public interface FridaModelTargetModuleContainer + extends FridaModelTargetEventScope, // + TargetModuleContainer, // + FridaEventsListenerAdapter { + + @Override + public CompletableFuture addSyntheticModule(String name); + + public FridaModelTargetModule getTargetModule(FridaModule module); + + public void moduleLoaded(FridaProcess proc, FridaModuleInfo info, int index, FridaCause cause); + + public void moduleReplaced(FridaProcess proc, FridaModuleInfo info, int index, FridaCause cause); + + public void moduleUnloaded(FridaProcess proc, FridaModuleInfo info, int index, FridaCause cause); + +} diff --git a/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/iface2/FridaModelTargetModuleSection.java b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/iface2/FridaModelTargetModuleSection.java new file mode 100644 index 0000000000..1493f31b62 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/iface2/FridaModelTargetModuleSection.java @@ -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.model.iface2; + +import ghidra.dbg.target.TargetSection; +import ghidra.program.model.address.AddressRange; + +public interface FridaModelTargetModuleSection extends FridaModelTargetObject, TargetSection { + + @Override + public AddressRange getRange(); + +} diff --git a/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/iface2/FridaModelTargetModuleSectionContainer.java b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/iface2/FridaModelTargetModuleSectionContainer.java new file mode 100644 index 0000000000..eab6180591 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/iface2/FridaModelTargetModuleSectionContainer.java @@ -0,0 +1,23 @@ +/* ### + * 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.model.iface2; + +import ghidra.dbg.target.TargetSectionContainer; + +public interface FridaModelTargetModuleSectionContainer + extends FridaModelTargetObject, TargetSectionContainer { + +} diff --git a/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/iface2/FridaModelTargetObject.java b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/iface2/FridaModelTargetObject.java new file mode 100644 index 0000000000..f498c89ee3 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/iface2/FridaModelTargetObject.java @@ -0,0 +1,87 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.frida.model.iface2; + +import java.util.List; +import java.util.Map; +import java.util.concurrent.CompletableFuture; + +import agent.frida.manager.impl.FridaManagerImpl; +import agent.frida.model.AbstractFridaModel; +import ghidra.async.AsyncUtils; +import ghidra.dbg.DebuggerModelListener; +import ghidra.dbg.agent.SpiTargetObject; +import ghidra.dbg.target.TargetObject; +import ghidra.dbg.util.CollectionUtils.Delta; +import ghidra.util.datastruct.ListenerSet; + +public interface FridaModelTargetObject extends SpiTargetObject { + + @Override + public AbstractFridaModel getModel(); + + public default CompletableFuture init(Map map) { + return CompletableFuture.completedFuture(null); + } + + public default FridaManagerImpl getManager() { + return (FridaManagerImpl) getModel().getManager(); + } + + public default FridaManagerImpl getManagerWithCheck() { + FridaManagerImpl impl = (FridaManagerImpl) getModel().getManager(); + if (impl == null) { + return impl; + } + return impl; + } + + public Delta changeAttributes(List remove, Map add, String reason); + + public CompletableFuture> requestNativeAttributes(); + + public default CompletableFuture requestAugmentedAttributes() { + return AsyncUtils.NIL; + } + + public CompletableFuture> requestNativeElements(); + + public ListenerSet getListeners(); + + public FridaModelTargetSession getParentSession(); + + public FridaModelTargetProcess getParentProcess(); + + public FridaModelTargetThread getParentThread(); + + public TargetObject getProxy(); + + public void setModified(Map map, boolean b); + + public void setModified(boolean modified); + + public void resetModified(); + + public Object getModelObject(); + + public void setModelObject(Object modelObject); + + public void addMapObject(Object object, TargetObject targetObject); + + public TargetObject getMapObject(Object object); + + public void deleteMapObject(Object object); +} diff --git a/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/iface2/FridaModelTargetProcess.java b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/iface2/FridaModelTargetProcess.java new file mode 100644 index 0000000000..92acbb0879 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/iface2/FridaModelTargetProcess.java @@ -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.model.iface2; + +import java.util.concurrent.CompletableFuture; + +import agent.frida.manager.FridaEventsListenerAdapter; +import agent.frida.manager.FridaProcess; +import agent.frida.model.iface1.*; +import ghidra.dbg.target.TargetAggregate; +import ghidra.dbg.target.TargetProcess; + +public interface FridaModelTargetProcess extends // + TargetAggregate, // + TargetProcess, // + FridaModelTargetExecutionStateful, // + //FridaModelTargetAccessConditioned, // + FridaModelTargetAttacher, // + FridaModelTargetAttachable, // + FridaModelTargetLauncher, // + FridaModelTargetDeletable, // + FridaModelTargetDetachable, // + FridaModelTargetKillable, // + FridaModelTargetResumable, // + //FridaModelTargetSteppable, // + //FridaModelTargetInterruptible, // + FridaEventsListenerAdapter, // + FridaModelSelectableObject { + + public void processStarted(FridaProcess proc); + + public FridaModelTargetThreadContainer getThreads(); + + public FridaModelTargetMemoryContainer getMemory(); + + public FridaProcess getProcess(); + + @Override + public CompletableFuture setActive(); + + +} diff --git a/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/iface2/FridaModelTargetProcessContainer.java b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/iface2/FridaModelTargetProcessContainer.java new file mode 100644 index 0000000000..6ec2823cd8 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/iface2/FridaModelTargetProcessContainer.java @@ -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.model.iface2; + +import agent.frida.manager.FridaEventsListenerAdapter; +import agent.frida.manager.FridaProcess; +import agent.frida.model.iface1.FridaModelTargetEventScope; + +public interface FridaModelTargetProcessContainer extends // + FridaModelTargetEventScope, // + FridaEventsListenerAdapter { + + public FridaModelTargetProcess getTargetProcess(FridaProcess process); + +} diff --git a/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/iface2/FridaModelTargetRegister.java b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/iface2/FridaModelTargetRegister.java new file mode 100644 index 0000000000..e9024f879a --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/iface2/FridaModelTargetRegister.java @@ -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.model.iface2; + +import java.math.BigInteger; + +import agent.frida.manager.FridaValue; +import ghidra.dbg.target.TargetRegister; +import ghidra.dbg.util.ConversionUtils; + +public interface FridaModelTargetRegister extends FridaModelTargetObject, TargetRegister { + + @Override + public int getBitLength(); + + public FridaValue getRegister(); + + public default byte[] getBytes() { + String val = (String) getCachedAttributes().get(VALUE_ATTRIBUTE_NAME); + BigInteger value = new BigInteger(val, 16); + return ConversionUtils.bigIntegerToBytes(getBitLength() / 8, value); + } + +} diff --git a/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/iface2/FridaModelTargetRegisterBank.java b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/iface2/FridaModelTargetRegisterBank.java new file mode 100644 index 0000000000..8954b46fe7 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/iface2/FridaModelTargetRegisterBank.java @@ -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.model.iface2; + +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; +import java.util.Map.Entry; +import java.util.concurrent.CompletableFuture; + +import agent.frida.manager.FridaReason; +import agent.frida.manager.FridaValue; +import agent.frida.manager.FridaState; +import ghidra.dbg.target.TargetRegisterBank; + +public interface FridaModelTargetRegisterBank extends FridaModelTargetObject, TargetRegisterBank { + + public FridaModelTargetRegister getTargetRegister(FridaValue register); + + public default void threadStateChangedSpecific(FridaState state, FridaReason reason) { + readRegistersNamed(getCachedElements().keySet()); + } + + @Override + public CompletableFuture> readRegistersNamed( + Collection names); + + @Override + public CompletableFuture writeRegistersNamed(Map values); + + @Override + public default Map getCachedRegisters() { + return getValues(); + } + + public default Map getValues() { + Map result = new HashMap<>(); + for (Entry entry : this.getCachedAttributes().entrySet()) { + if (entry.getValue() instanceof FridaModelTargetRegister) { + FridaModelTargetRegister reg = (FridaModelTargetRegister) entry.getValue(); + byte[] bytes = reg.getBytes(); + result.put(entry.getKey(), bytes); + } + } + return result; + } + +} diff --git a/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/iface2/FridaModelTargetRegisterContainer.java b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/iface2/FridaModelTargetRegisterContainer.java new file mode 100644 index 0000000000..3a7eb8bc3f --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/iface2/FridaModelTargetRegisterContainer.java @@ -0,0 +1,23 @@ +/* ### + * 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.model.iface2; + +import ghidra.dbg.target.TargetRegisterContainer; + +public interface FridaModelTargetRegisterContainer + extends FridaModelTargetObject, TargetRegisterContainer { + +} diff --git a/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/iface2/FridaModelTargetRegisterContainerAndBank.java b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/iface2/FridaModelTargetRegisterContainerAndBank.java new file mode 100644 index 0000000000..891343a6ff --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/iface2/FridaModelTargetRegisterContainerAndBank.java @@ -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.model.iface2; + +import java.util.Map.Entry; + +import ghidra.dbg.target.TargetRegisterBank; +import ghidra.dbg.target.TargetRegisterContainer; + +public interface FridaModelTargetRegisterContainerAndBank extends // + FridaModelTargetObject, // + TargetRegisterContainer, // + TargetRegisterBank { + + @SuppressWarnings("rawtypes") + public FridaModelTargetRegister getTargetRegister(Entry register); + +} diff --git a/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/iface2/FridaModelTargetRoot.java b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/iface2/FridaModelTargetRoot.java new file mode 100644 index 0000000000..a9c33b2c4f --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/iface2/FridaModelTargetRoot.java @@ -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.model.iface2; + +import agent.frida.manager.FridaEventsListenerAdapter; +import agent.frida.model.iface1.*; + +public interface FridaModelTargetRoot extends // + //FridaModelTargetObject, + FridaModelTargetAccessConditioned, // + FridaModelTargetAttacher, // + FridaModelTargetActiveScope, // + FridaModelTargetEventScope, // + FridaModelTargetLauncher, // + FridaModelTargetFocusScope, // + FridaEventsListenerAdapter { + + void setDefaultConnector(FridaModelTargetConnector defaultConnector); + + // getActive & requestActivation implemented by FridaModelTargetObject & FridaModelTargetActiveScope + // getFocus & requestFocus implemented by FridaModelTargetObject & FridaModelTargetFocusScope +} diff --git a/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/iface2/FridaModelTargetSession.java b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/iface2/FridaModelTargetSession.java new file mode 100644 index 0000000000..91a837c9d3 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/iface2/FridaModelTargetSession.java @@ -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.model.iface2; + +import java.util.List; +import java.util.Map; + +import agent.frida.frida.FridaClient.DebugOutputFlags; +import agent.frida.manager.FridaEventsListenerAdapter; +import agent.frida.model.iface1.*; +import ghidra.dbg.target.TargetAggregate; +import ghidra.dbg.target.TargetConsole; +import ghidra.dbg.target.TargetConsole.Channel; + +public interface FridaModelTargetSession extends // + //FridaModelTargetAccessConditioned, // + //FridaModelTargetFocusScope, // + FridaModelTargetExecutionStateful, // + FridaModelTargetInterpreter, // + //FridaModelTargetInterruptible, // + FridaModelTargetResumable, // + FridaEventsListenerAdapter, // + FridaModelSelectableObject, // + TargetAggregate { + + FridaModelTargetProcessContainer getProcesses(); + + FridaModelTargetModuleContainer getModules(); + + @Override + public default void consoleOutput(String output, int mask) { + + Channel chan = TargetConsole.Channel.STDOUT; + if (((mask & DebugOutputFlags.DEBUG_OUTPUT_ERROR.getValue()) // + == DebugOutputFlags.DEBUG_OUTPUT_ERROR.getValue()) || // + ((mask & DebugOutputFlags.DEBUG_OUTPUT_WARNING.getValue()) // + == DebugOutputFlags.DEBUG_OUTPUT_WARNING.getValue())) { + chan = TargetConsole.Channel.STDERR; + } + getListeners().fire.consoleOutput(getProxy(), chan, output); + } + + @Override + public default void promptChanged(String prompt) { + changeAttributes(List.of(), Map.of( // + PROMPT_ATTRIBUTE_NAME, prompt // + ), "Refreshed"); + } + +} diff --git a/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/iface2/FridaModelTargetSessionAttributes.java b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/iface2/FridaModelTargetSessionAttributes.java new file mode 100644 index 0000000000..24ca7fa58a --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/iface2/FridaModelTargetSessionAttributes.java @@ -0,0 +1,25 @@ +/* ### + * 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.model.iface2; + +import agent.frida.manager.FridaEventsListenerAdapter; +import agent.frida.model.iface1.FridaModelTargetEnvironment; + +public interface FridaModelTargetSessionAttributes extends + FridaModelTargetEnvironment, // + FridaEventsListenerAdapter { + +} diff --git a/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/iface2/FridaModelTargetSessionAttributesEnvironment.java b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/iface2/FridaModelTargetSessionAttributesEnvironment.java new file mode 100644 index 0000000000..964a49e716 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/iface2/FridaModelTargetSessionAttributesEnvironment.java @@ -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. + */ +package agent.frida.model.iface2; + +import agent.frida.manager.FridaEventsListenerAdapter; + +public interface FridaModelTargetSessionAttributesEnvironment extends // + FridaModelTargetObject, // + FridaEventsListenerAdapter { + +} diff --git a/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/iface2/FridaModelTargetSessionAttributesPlatform.java b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/iface2/FridaModelTargetSessionAttributesPlatform.java new file mode 100644 index 0000000000..c4fed82892 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/iface2/FridaModelTargetSessionAttributesPlatform.java @@ -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. + */ +package agent.frida.model.iface2; + +import agent.frida.manager.FridaEventsListenerAdapter; + +public interface FridaModelTargetSessionAttributesPlatform extends // + FridaModelTargetObject, // + FridaEventsListenerAdapter { + +} diff --git a/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/iface2/FridaModelTargetSessionContainer.java b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/iface2/FridaModelTargetSessionContainer.java new file mode 100644 index 0000000000..74ced9322f --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/iface2/FridaModelTargetSessionContainer.java @@ -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.model.iface2; + +import agent.frida.manager.FridaCause; +import agent.frida.manager.FridaEventsListenerAdapter; +import agent.frida.manager.FridaSession; + +public interface FridaModelTargetSessionContainer + extends FridaModelTargetObject, FridaEventsListenerAdapter { + + @Override + public void sessionAdded(FridaSession session, FridaCause cause); + + @Override + public void sessionReplaced(FridaSession session, FridaCause cause); + + @Override + public void sessionRemoved(String sessionId, FridaCause cause); + + public FridaModelTargetSession getTargetSession(FridaSession session); + +} diff --git a/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/iface2/FridaModelTargetStack.java b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/iface2/FridaModelTargetStack.java new file mode 100644 index 0000000000..11a04f7f74 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/iface2/FridaModelTargetStack.java @@ -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.model.iface2; + +import agent.frida.manager.FridaFrame; +import ghidra.dbg.target.TargetStack; + +public interface FridaModelTargetStack + extends FridaModelTargetObject, TargetStack { + + public FridaModelTargetStackFrame getTargetFrame(FridaFrame frame); + +} diff --git a/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/iface2/FridaModelTargetStackFrame.java b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/iface2/FridaModelTargetStackFrame.java new file mode 100644 index 0000000000..4938c97b29 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/iface2/FridaModelTargetStackFrame.java @@ -0,0 +1,102 @@ +/* ### + * 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.model.iface2; + +import java.util.Map; +import java.util.concurrent.CompletableFuture; + +import agent.frida.manager.FridaEventsListenerAdapter; +import agent.frida.manager.FridaFrame; +import agent.frida.model.iface1.FridaModelSelectableObject; +import ghidra.dbg.target.TargetObject; +import ghidra.dbg.target.TargetStackFrame; +import ghidra.program.model.address.Address; +import ghidra.program.model.address.AddressSpace; + +public interface FridaModelTargetStackFrame extends // + TargetStackFrame, // + FridaEventsListenerAdapter, // + FridaModelSelectableObject { + + public static final String FUNC_ATTRIBUTE_NAME = "function"; + public static final String FILE_ATTRIBUTE_NAME = "file"; + public static final String MODULE_ATTRIBUTE_NAME = "module"; + public static final String LINE_ATTRIBUTE_NAME = "line"; + public static final String FUNC_TABLE_ENTRY_ATTRIBUTE_NAME = "Table Entry"; + public static final String FRAME_OFFSET_ATTRIBUTE_NAME = "Frame Offset"; + public static final String INST_OFFSET_ATTRIBUTE_NAME = "Inst. Offset"; + public static final String RETURN_OFFSET_ATTRIBUTE_NAME = "Return Offset"; + public static final String CALL_FRAME_OFFSET_ATTRIBUTE_NAME = "Call Frame Offset"; + public static final String STACK_OFFSET_ATTRIBUTE_NAME = "Stack Offset"; + public static final String VIRTUAL_ATTRIBUTE_NAME = "Virtual"; + public static final String PARAM0_ATTRIBUTE_NAME = "Param[0]"; + public static final String PARAM1_ATTRIBUTE_NAME = "Param[1]"; + public static final String PARAM2_ATTRIBUTE_NAME = "Param[2]"; + public static final String PARAM3_ATTRIBUTE_NAME = "Param[3]"; + + @Override + public default CompletableFuture setActive() { + return CompletableFuture.completedFuture(null); + } + + @Override + public default CompletableFuture init(Map map) { + AddressSpace space = getModel().getAddressSpace("ram"); + return requestNativeAttributes().thenCompose(attrs -> { + if (attrs == null) { + return CompletableFuture.completedFuture(null); + } + map.putAll(attrs); + FridaModelTargetObject attributes = (FridaModelTargetObject) attrs.get("Attributes"); + if (attributes == null) { + return CompletableFuture.completedFuture(null); + } + return attributes.requestAugmentedAttributes().thenCompose(ax -> { + Map subattrs = attributes.getCachedAttributes(); + if (subattrs == null) { + return CompletableFuture.completedFuture(null); + } + FridaModelTargetObject frameNumber = + (FridaModelTargetObject) subattrs.get("FrameNumber"); + return frameNumber.requestAugmentedAttributes().thenCompose(bx -> { + Object noval = frameNumber.getCachedAttribute(VALUE_ATTRIBUTE_NAME); + String nostr = noval.toString(); + FridaModelTargetObject instructionOffset = + (FridaModelTargetObject) subattrs.get("InstructionOffset"); + return instructionOffset.requestAugmentedAttributes().thenAccept(cx -> { + String oldval = (String) getCachedAttribute(DISPLAY_ATTRIBUTE_NAME); + Object pcval = instructionOffset.getCachedAttribute(VALUE_ATTRIBUTE_NAME); + String pcstr = pcval.toString(); + long pc = Long.parseUnsignedLong(pcstr, 16); + map.put(PC_ATTRIBUTE_NAME, space.getAddress(pc)); + String display = String.format("#%s 0x%s", nostr, pcstr); + map.put(DISPLAY_ATTRIBUTE_NAME, display); + setModified(map, !display.equals(oldval)); + }); + }); + }); + }); + } + + public void setFrame(FridaFrame frame); + + public TargetObject getThread(); + + public Address getPC(); + + public FridaModelTargetProcess getProcess(); + +} diff --git a/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/iface2/FridaModelTargetStackFrameRegister.java b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/iface2/FridaModelTargetStackFrameRegister.java new file mode 100644 index 0000000000..78b0067ff9 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/iface2/FridaModelTargetStackFrameRegister.java @@ -0,0 +1,50 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.frida.model.iface2; + +import java.math.BigInteger; + +import agent.frida.manager.FridaValue; +import ghidra.dbg.target.schema.TargetAttributeType; +import ghidra.dbg.target.schema.TargetElementType; +import ghidra.dbg.target.schema.TargetObjectSchema.ResyncMode; +import ghidra.dbg.target.schema.TargetObjectSchemaInfo; +import ghidra.dbg.util.ConversionUtils; + +@TargetObjectSchemaInfo( + name = "RegisterValueInterface", + elementResync = ResyncMode.ALWAYS, + elements = { + @TargetElementType(type = Void.class) + }, + attributes = { + @TargetAttributeType(type = Object.class) + }, + canonicalContainer = true) +public interface FridaModelTargetStackFrameRegister extends FridaModelTargetRegister { + + @Override + public int getBitLength(); + + public FridaValue getRegister(); + + public default byte[] getBytes() { + String val = (String) getCachedAttributes().get(VALUE_ATTRIBUTE_NAME); + BigInteger value = new BigInteger(val, 16); + return ConversionUtils.bigIntegerToBytes(16, value); + } + +} diff --git a/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/iface2/FridaModelTargetStackFrameRegisterBank.java b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/iface2/FridaModelTargetStackFrameRegisterBank.java new file mode 100644 index 0000000000..3a116773b0 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/iface2/FridaModelTargetStackFrameRegisterBank.java @@ -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.model.iface2; + +import agent.frida.manager.FridaValue; +import agent.frida.model.impl.FridaModelTargetRegisterImpl; +import ghidra.dbg.target.schema.TargetAttributeType; +import ghidra.dbg.target.schema.TargetElementType; +import ghidra.dbg.target.schema.TargetObjectSchema.ResyncMode; +import ghidra.dbg.target.schema.TargetObjectSchemaInfo; + +@TargetObjectSchemaInfo( + name = "RegisterValueBankInterface", + elementResync = ResyncMode.ALWAYS, + elements = { + @TargetElementType(type = FridaModelTargetRegisterImpl.class) + }, + attributes = { + @TargetAttributeType(type = Object.class) + }, + canonicalContainer = true) +public interface FridaModelTargetStackFrameRegisterBank + extends FridaModelTargetRegisterBank { + + public FridaModelTargetRegister getTargetRegister(FridaValue register); + +} diff --git a/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/iface2/FridaModelTargetStackFrameRegisterContainer.java b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/iface2/FridaModelTargetStackFrameRegisterContainer.java new file mode 100644 index 0000000000..873359f4bc --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/iface2/FridaModelTargetStackFrameRegisterContainer.java @@ -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.model.iface2; + + +import agent.frida.manager.FridaValue; +import ghidra.dbg.target.schema.TargetAttributeType; +import ghidra.dbg.target.schema.TargetObjectSchema.ResyncMode; +import ghidra.dbg.target.schema.TargetObjectSchemaInfo; + +@TargetObjectSchemaInfo( + name = "RegisterContainerInterface", + attributeResync = ResyncMode.ALWAYS, + attributes = { + @TargetAttributeType( + name = "General Purpose Registers", + type = FridaModelTargetStackFrameRegisterBank.class, + required = true), + @TargetAttributeType( + name = "Exception State Registers", + type = FridaModelTargetStackFrameRegisterBank.class, + required = true), + @TargetAttributeType( + name = "Floating Point Registers", + type = FridaModelTargetStackFrameRegisterBank.class, + required = true), + @TargetAttributeType(type = Void.class) + }) +public interface FridaModelTargetStackFrameRegisterContainer + extends FridaModelTargetRegisterContainer { + + public FridaModelTargetObject getTargetRegisterBank(FridaValue val); + +} diff --git a/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/iface2/FridaModelTargetSymbol.java b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/iface2/FridaModelTargetSymbol.java new file mode 100644 index 0000000000..e2f29ae8fb --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/iface2/FridaModelTargetSymbol.java @@ -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.model.iface2; + +import ghidra.dbg.target.TargetSymbol; +import ghidra.program.model.address.Address; + +public interface FridaModelTargetSymbol extends FridaModelTargetObject, TargetSymbol { + + @Override + public boolean isConstant(); + + @Override + public Address getValue(); + + @Override + public long getSize(); +} diff --git a/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/iface2/FridaModelTargetSymbolContainer.java b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/iface2/FridaModelTargetSymbolContainer.java new file mode 100644 index 0000000000..72cac9b03b --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/iface2/FridaModelTargetSymbolContainer.java @@ -0,0 +1,27 @@ +/* ### + * 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.model.iface2; + +import agent.frida.manager.FridaSymbol; +import agent.frida.model.impl.FridaModelTargetSymbolImpl; +import ghidra.dbg.target.TargetSymbolNamespace; + +public interface FridaModelTargetSymbolContainer + extends FridaModelTargetObject, TargetSymbolNamespace { + + public FridaModelTargetSymbolImpl getTargetSymbol(FridaSymbol symbol); + +} diff --git a/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/iface2/FridaModelTargetThread.java b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/iface2/FridaModelTargetThread.java new file mode 100644 index 0000000000..dd54b7ce66 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/iface2/FridaModelTargetThread.java @@ -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.model.iface2; + +import java.util.concurrent.CompletableFuture; + +import agent.frida.manager.*; +import agent.frida.model.iface1.FridaModelSelectableObject; +import agent.frida.model.iface1.FridaModelTargetExecutionStateful; +import agent.frida.model.impl.FridaModelTargetStackImpl; +import ghidra.dbg.target.*; + +public interface FridaModelTargetThread extends // + TargetThread, // + //FridaModelTargetAccessConditioned, // + FridaModelTargetExecutionStateful, // + //FridaModelTargetSteppable, // + FridaStateListener, // + FridaEventsListenerAdapter, // + FridaModelSelectableObject { + + public default FridaThread getThread() { + return (FridaThread) getModelObject(); + } + + public default void threadStateChangedSpecific(FridaState state, FridaReason reason) { + TargetRegisterContainer container = + (TargetRegisterContainer) getCachedAttribute("Registers"); + TargetRegisterBank bank = (TargetRegisterBank) container.getCachedAttribute("User"); + if (state.equals(FridaState.FRIDA_THREAD_STOPPED)) { + bank.readRegistersNamed(getCachedElements().keySet()); + } + } + + @Override + public default CompletableFuture setActive() { + return CompletableFuture.completedFuture(null); + } + + public FridaModelTargetStackImpl getStack(); + + public String getExecutingProcessorType(); + +} diff --git a/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/iface2/FridaModelTargetThreadContainer.java b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/iface2/FridaModelTargetThreadContainer.java new file mode 100644 index 0000000000..4f68592ec1 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/iface2/FridaModelTargetThreadContainer.java @@ -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.model.iface2; + +import agent.frida.manager.*; +import agent.frida.model.iface1.FridaModelTargetEventScope; + +public interface FridaModelTargetThreadContainer extends // + FridaModelTargetEventScope, // + FridaEventsListenerAdapter { + + public FridaModelTargetThread getTargetThread(FridaThread thread); + + public void threadCreated(FridaThread thread, FridaCause cause); + + public void threadReplaced(FridaThread thread, FridaCause cause); + + public void threadExited(FridaThread thread, FridaCause caus); + +} diff --git a/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/impl/FridaDebuggerBot.java b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/impl/FridaDebuggerBot.java new file mode 100644 index 0000000000..cf1527cb57 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/impl/FridaDebuggerBot.java @@ -0,0 +1,201 @@ +/* ### + * 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.model.impl; + +import java.lang.invoke.MethodHandles; +import java.util.*; + +import agent.frida.frida.FridaThreadInfo; +import agent.frida.manager.FridaProcess; +import agent.frida.manager.FridaThread; +import agent.frida.manager.evt.FridaStateChangedEvent; +import agent.frida.manager.evt.FridaThreadSelectedEvent; +import agent.frida.manager.impl.FridaManagerImpl; +import ghidra.app.plugin.core.debug.service.workflow.DebuggerWorkflowServicePlugin; +import ghidra.app.services.DebuggerBot; +import ghidra.app.services.DebuggerBotInfo; +import ghidra.dbg.*; +import ghidra.dbg.error.DebuggerMemoryAccessException; +import ghidra.dbg.target.*; +import ghidra.dbg.target.TargetConsole.Channel; +import ghidra.dbg.target.TargetExecutionStateful.TargetExecutionState; +import ghidra.dbg.util.DebuggerCallbackReorderer; +import ghidra.framework.options.annotation.HelpInfo; +import ghidra.program.model.address.Address; +import ghidra.program.model.address.AddressRange; +import ghidra.util.datastruct.PrivatelyQueuedListener; + +@DebuggerBotInfo( // + description = "Link debugger to Frida", // + details = "Listens for debuggers to add state to Frida.", // + help = @HelpInfo(anchor = "link_frida"), // + enabledByDefault = true // +) +public class FridaDebuggerBot implements DebuggerBot { + private DebuggerWorkflowServicePlugin plugin; + + private FridaObjectListener listener = new FridaObjectListener(); + private List models = new ArrayList<>(); + private FridaModelImpl primary; + private FridaManagerImpl manager; + + @Override + public void enable(DebuggerWorkflowServicePlugin wp) { + this.plugin = wp; + } + + @Override + public boolean isEnabled() { + return plugin != null; + } + + @Override + public void disable() { + plugin = null; + } + + @Override + public void modelAdded(DebuggerObjectModel model) { + models.add(model); + if (model instanceof FridaModelImpl) { + primary = (FridaModelImpl) model; + manager = primary.getManager(); + } else { + model.addModelListener(getListener(), true); + } + } + + @Override + public void modelRemoved(DebuggerObjectModel model) { + models.remove(model); + if (model instanceof FridaModelImpl) { + primary = null; + manager = null; + } else { + model.removeModelListener(getListener()); + } + } + + public DebuggerModelListener getListener() { + return listener.queue.in; + } + + private Object findMatchingObject(TargetObject object) { + if (object instanceof TargetProcess) { + String id = Long.toHexString(((TargetProcess) object).getPid()); + return manager.getProcess(manager.getCurrentSession(), id); + } + if (object instanceof TargetThread) { + TargetProcess tp = DebugModelConventions.ancestor(TargetProcess.class, object); + Object found = findMatchingObject(tp); + if (found != null) { + FridaProcess process = (FridaProcess) found; + String id = Long.toHexString(((TargetThread) object).getTid()); + return manager.getThread(process, id); + } + } + return null; + } + + class FridaObjectListener extends AnnotatedDebuggerAttributeListener { + protected final DebuggerCallbackReorderer reorderer = new DebuggerCallbackReorderer(this); + protected final PrivatelyQueuedListener queue = + new PrivatelyQueuedListener<>(DebuggerModelListener.class, "ObjectsProvider-EventQueue", + reorderer); + + public FridaObjectListener() { + super(MethodHandles.lookup()); + } + + @AttributeCallback(TargetAccessConditioned.ACCESSIBLE_ATTRIBUTE_NAME) + public void accessibilityChanged(TargetObject object, boolean accessible) { + //System.err.println("accessibilityChanged: "+object+":"+accessible); + } + + @Override + public void consoleOutput(TargetObject console, Channel channel, String out) { + //System.err.println("consoleOutput: "+out); + } + + @AttributeCallback(TargetObject.DISPLAY_ATTRIBUTE_NAME) + public void displayChanged(TargetObject object, String display) { + //System.err.println("displayChanged: "+display); + } + + @AttributeCallback(TargetObject.MODIFIED_ATTRIBUTE_NAME) + public void modifiedChanged(TargetObject object, boolean modified) { + //System.err.println("modifiedChanged: "+object); + } + + @AttributeCallback(TargetExecutionStateful.STATE_ATTRIBUTE_NAME) + public void executionStateChanged(TargetObject object, TargetExecutionState state) { + if (primary != null) { + Object localObject = findMatchingObject(object); + if (localObject != null) { + manager.processEvent(new FridaStateChangedEvent(localObject, state)); + } + } + } + + @AttributeCallback(TargetFocusScope.FOCUS_ATTRIBUTE_NAME) + public void focusChanged(TargetObject object, TargetObject focused) { + if (primary != null) { + if (focused instanceof TargetThread) { + Object localObject = findMatchingObject(focused); + if (localObject != null) { + FridaThreadInfo info = new FridaThreadInfo((FridaThread) localObject); + manager.processEvent(new FridaThreadSelectedEvent(info)); + } + } + } + } + + @Override + public void memoryUpdated(TargetObject memory, Address address, byte[] data) { + //System.err.println("memoryUpdated: "+address); + } + + @Override + public void memoryReadError(TargetObject memory, AddressRange range, + DebuggerMemoryAccessException e) { + //System.err.println("memoryReadError: "+range); + } + + @AttributeCallback(TargetInterpreter.PROMPT_ATTRIBUTE_NAME) + public void promptChanged(TargetObject interpreter, String prompt) { + //System.err.println("promptChanged: "+prompt); + } + + @Override + public void registersUpdated(TargetObject bank, Map updates) { + //System.err.println("registersUpdated: "+bank); + } + + @Override + public void elementsChanged(TargetObject parent, Collection removed, + Map added) { + //System.err.println("elementsChanged: "+parent); + } + + @Override + public void attributesChanged(TargetObject parent, Collection removed, + Map added) { + super.attributesChanged(parent, removed, added); + //System.err.println("attributesChanged: "+parent); + } + } + +} diff --git a/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/impl/FridaModelDefaultTargetModelRoot.java b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/impl/FridaModelDefaultTargetModelRoot.java new file mode 100644 index 0000000000..541a511cb0 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/impl/FridaModelDefaultTargetModelRoot.java @@ -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.model.impl; + +import ghidra.dbg.target.TargetAggregate; +import ghidra.dbg.target.schema.TargetObjectSchema; + +public class FridaModelDefaultTargetModelRoot extends FridaModelTargetObjectImpl + implements TargetAggregate { + + public FridaModelDefaultTargetModelRoot(FridaModelImpl model, String typeHint) { + super(model, null, null, null, typeHint); + } + + public FridaModelDefaultTargetModelRoot(FridaModelImpl model, String typeHint, + TargetObjectSchema schema) { + super(model, null, null, null, typeHint, schema); + } + +} diff --git a/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/impl/FridaModelImpl.java b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/impl/FridaModelImpl.java new file mode 100644 index 0000000000..1923e68cde --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/impl/FridaModelImpl.java @@ -0,0 +1,185 @@ +/* ### + * 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.model.impl; + +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.RejectedExecutionException; + +import org.apache.commons.lang3.exception.ExceptionUtils; + +import agent.frida.frida.FridaClient; +import agent.frida.manager.FridaManager; +import agent.frida.manager.impl.FridaManagerImpl; +import agent.frida.model.AbstractFridaModel; +import agent.frida.model.iface2.FridaModelTargetProcess; +import agent.frida.model.iface2.FridaModelTargetSession; +import ghidra.async.AsyncUtils; +import ghidra.dbg.DebuggerModelClosedReason; +import ghidra.dbg.DebuggerObjectModelWithMemory; +import ghidra.dbg.error.DebuggerModelTerminatingException; +import ghidra.dbg.target.TargetMemory; +import ghidra.dbg.target.TargetObject; +import ghidra.dbg.target.schema.AnnotatedSchemaContext; +import ghidra.dbg.target.schema.TargetObjectSchema; +import ghidra.program.model.address.*; + +public class FridaModelImpl extends AbstractFridaModel implements DebuggerObjectModelWithMemory { + // The model must convert to and from Ghidra's address space names + protected static final String SPACE_NAME = "ram"; + + protected static final AnnotatedSchemaContext SCHEMA_CTX = new AnnotatedSchemaContext(); + protected static final TargetObjectSchema ROOT_SCHEMA = + SCHEMA_CTX.getSchemaForClass(FridaModelTargetRootImpl.class); + + // Don't make this static, so each model has a unique "Frida" space + protected final AddressSpace space = + new GenericAddressSpace(SPACE_NAME, 64, AddressSpace.TYPE_RAM, 0); + protected final AddressFactory addressFactory = + new DefaultAddressFactory(new AddressSpace[] { space }); + + protected FridaManager manager; + protected FridaModelTargetSession session; + + protected final FridaModelTargetRootImpl root; + protected final CompletableFuture completedRoot; + + protected Map objectMap = new HashMap<>(); + + public FridaModelImpl() { + this.manager = FridaManager.newInstance(); + this.root = new FridaModelTargetRootImpl(this, ROOT_SCHEMA); + this.completedRoot = CompletableFuture.completedFuture(root); + addModelRoot(root); + } + + @Override + public String getBrief() { + return "FRIDA@" + Integer.toHexString(System.identityHashCode(this)); + } + + @Override + public AddressSpace getAddressSpace(String name) { + if (!SPACE_NAME.equals(name)) { + return null; + } + return space; + } + + @Override + public AddressFactory getAddressFactory() { + return addressFactory; + } + + @Override + public CompletableFuture startFrida(String[] args) { + return manager.start(args).thenApplyAsync(__ -> null, clientExecutor); + } + + @Override + public boolean isRunning() { + return manager.isRunning(); + } + + @Override + public void terminate() throws IOException { + listeners.fire.modelClosed(DebuggerModelClosedReason.NORMAL); + root.invalidateSubtree(root, "Frida is terminating"); + manager.terminate(); + } + + @Override + public TargetObjectSchema getRootSchema() { + return root.getSchema(); + } + + @Override + public CompletableFuture fetchModelRoot() { + return completedRoot; + } + + @Override + public FridaManagerImpl getManager() { + return (FridaManagerImpl) manager; + } + + @Override + public CompletableFuture close() { + try { + terminate(); + return super.close(); + } + catch (RejectedExecutionException e) { + reportError(this, "Model is already closing", e); + return AsyncUtils.NIL; + } + catch (Throwable t) { + return CompletableFuture.failedFuture(t); + } + } + + @Override + public FridaModelTargetSession getSession() { + return session; + } + + @Override + public TargetMemory getMemory(TargetObject target, Address address, int length) { + if (target instanceof FridaModelTargetProcess) { + FridaModelTargetProcess process = (FridaModelTargetProcess) target; + return new FridaModelTargetMemoryContainerImpl(process); + } + return null; + } + + @Override + public void addModelObject(Object object, TargetObject targetObject) { + if (object == null) { + return; + } + objectMap.put(FridaClient.getModelKey(object), targetObject); + } + + @Override + public TargetObject getModelObject(Object object) { + if (object == null) { + return null; + } + return objectMap.get(FridaClient.getModelKey(object)); + } + + public void deleteModelObject(Object object) { + if (object == null) { + return; + } + objectMap.remove(FridaClient.getModelKey(object)); + } + + @Override + public CompletableFuture gateFuture(CompletableFuture future) { + return super.gateFuture(future).exceptionally(ex -> { + for (Throwable cause = ex; cause != null; cause = cause.getCause()) { + if (cause instanceof RejectedExecutionException) { + throw new DebuggerModelTerminatingException("Frida is terminating", ex); + } + } + return ExceptionUtils.rethrow(ex); + }); + } + +} diff --git a/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/impl/FridaModelTargetAvailableContainerImpl.java b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/impl/FridaModelTargetAvailableContainerImpl.java new file mode 100644 index 0000000000..068e53ff78 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/impl/FridaModelTargetAvailableContainerImpl.java @@ -0,0 +1,104 @@ +/* ### + * 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.model.impl; + +import java.util.List; +import java.util.Map; +import java.util.concurrent.CompletableFuture; +import java.util.stream.Collectors; + +import org.apache.commons.lang3.tuple.Pair; + +import agent.frida.model.iface1.FridaModelTargetConfigurable; +import agent.frida.model.iface2.FridaModelTargetAvailable; +import agent.frida.model.iface2.FridaModelTargetAvailableContainer; +import agent.frida.model.iface2.FridaModelTargetRoot; +import ghidra.async.AsyncUtils; +import ghidra.dbg.error.DebuggerIllegalArgumentException; +import ghidra.dbg.target.TargetConfigurable; +import ghidra.dbg.target.TargetObject; +import ghidra.dbg.target.schema.TargetAttributeType; +import ghidra.dbg.target.schema.TargetElementType; +import ghidra.dbg.target.schema.TargetObjectSchema.ResyncMode; +import ghidra.dbg.target.schema.TargetObjectSchemaInfo; +import ghidra.util.datastruct.WeakValueHashMap; + +@TargetObjectSchemaInfo( + name = "AvailableContainer", + elements = { + @TargetElementType(type = FridaModelTargetAvailableImpl.class) // + }, + elementResync = ResyncMode.ALWAYS, + attributes = { // + @TargetAttributeType(name = TargetConfigurable.BASE_ATTRIBUTE_NAME, type = Integer.class), // + @TargetAttributeType(type = Void.class) // + }, + canonicalContainer = true) +public class FridaModelTargetAvailableContainerImpl extends FridaModelTargetObjectImpl + implements FridaModelTargetAvailableContainer, FridaModelTargetConfigurable { + + protected final Map attachablesById = + new WeakValueHashMap<>(); + + public FridaModelTargetAvailableContainerImpl(FridaModelTargetRoot root) { + super(root.getModel(), root, "Available", "AvailableContainer"); + this.changeAttributes(List.of(), Map.of(BASE_ATTRIBUTE_NAME, 16), "Initialized"); + } + + @Override + public CompletableFuture requestElements(boolean refresh) { + return getManager().listAvailableProcesses().thenAccept(list -> { + List available; + synchronized (this) { + // NOTE: If more details added to entries, should clear attachablesById + available = + list.stream().map(this::getTargetAttachableEx).collect(Collectors.toList()); + } + setElements(available, Map.of(), "Refreshed"); + }); + } + + public synchronized FridaModelTargetAvailable getTargetAttachableEx(Pair pair) { + return attachablesById.computeIfAbsent(pair.getLeft(), + i -> new FridaModelTargetAvailableImpl(this, pair.getLeft(), pair.getRight())); + } + + @Override + public synchronized FridaModelTargetAvailable getTargetAttachable(String pid) { + return attachablesById.computeIfAbsent(pid, + i -> new FridaModelTargetAvailableImpl(this, pid)); + } + + @Override + public CompletableFuture writeConfigurationOption(String key, Object value) { + switch (key) { + case BASE_ATTRIBUTE_NAME: + if (value instanceof Integer) { + this.changeAttributes(List.of(), Map.of(BASE_ATTRIBUTE_NAME, value), + "Modified"); + for (FridaModelTargetAvailable child : attachablesById.values()) { + child.setBase(value); + } + } + else { + throw new DebuggerIllegalArgumentException("Base should be numeric"); + } + default: + } + return AsyncUtils.NIL; + } + +} diff --git a/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/impl/FridaModelTargetAvailableImpl.java b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/impl/FridaModelTargetAvailableImpl.java new file mode 100644 index 0000000000..ba78043177 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/impl/FridaModelTargetAvailableImpl.java @@ -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.model.impl; + +import java.util.List; +import java.util.Map; + +import agent.frida.model.iface2.FridaModelTargetAvailable; +import agent.frida.model.iface2.FridaModelTargetAvailableContainer; +import ghidra.dbg.target.schema.*; +import ghidra.dbg.util.PathUtils; + +@TargetObjectSchemaInfo( + name = "Available", + elements = { + @TargetElementType(type = Void.class) }, + attributes = { + @TargetAttributeType(type = Void.class) }) +public class FridaModelTargetAvailableImpl extends FridaModelTargetObjectImpl + implements FridaModelTargetAvailable { + + protected static String keyAttachable(String pid) { + return PathUtils.makeKey(pid); + } + + protected final String pid; + protected final String name; + private Integer base = 10; + + public FridaModelTargetAvailableImpl(FridaModelTargetAvailableContainer parent, String pid, + String name) { + super(parent.getModel(), parent, keyAttachable(pid), name); + this.name = name; + this.pid = pid; + + this.changeAttributes(List.of(), List.of(), Map.of(// + PID_ATTRIBUTE_NAME, Long.parseLong(pid, 10), // + DISPLAY_ATTRIBUTE_NAME, getDisplay() // + ), "Initialized"); + } + + public FridaModelTargetAvailableImpl(FridaModelTargetAvailableContainer parent, String pid) { + super(parent.getModel(), parent, keyAttachable(pid), "Attachable"); + this.pid = pid; + this.name = ""; + + this.changeAttributes(List.of(), List.of(), Map.of(// + PID_ATTRIBUTE_NAME, Long.parseLong(pid, 10), // + DISPLAY_ATTRIBUTE_NAME, keyAttachable(pid) // + ), "Initialized"); + } + + @TargetAttributeType(name = PID_ATTRIBUTE_NAME, hidden = true) + @Override + public Long getPid() { + return Long.parseLong(pid); + } + + @Override + public String getDisplay() { + Long p = Long.decode(pid); + String pidstr = ""; + if (base == 16) { + pidstr = "0x" + Long.toHexString(p); + } else { + pidstr = pid; + } + return "[" + pidstr + "] : " + name.trim(); + } + + public void setBase(Object value) { + this.base = (Integer) value; + changeAttributes(List.of(), List.of(), Map.of( // + DISPLAY_ATTRIBUTE_NAME, getDisplay()// + ), "Started"); + } + +} diff --git a/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/impl/FridaModelTargetConnectorContainerImpl.java b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/impl/FridaModelTargetConnectorContainerImpl.java new file mode 100644 index 0000000000..aa65e0b603 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/impl/FridaModelTargetConnectorContainerImpl.java @@ -0,0 +1,87 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.frida.model.impl; + +import java.util.List; +import java.util.Map; + +import agent.frida.model.iface2.FridaModelTargetConnector; +import agent.frida.model.iface2.FridaModelTargetRoot; +import ghidra.dbg.target.schema.TargetAttributeType; +import ghidra.dbg.target.schema.TargetObjectSchemaInfo; + +@TargetObjectSchemaInfo( + name = "ConnectorContainer", + attributes = { + @TargetAttributeType( + name = "Launch process", + type = FridaModelTargetProcessLaunchConnectorImpl.class, + required = true, + fixed = true), + @TargetAttributeType( + name = "Launch process w/ options", + type = FridaModelTargetProcessLaunchWithOptionsConnectorImpl.class, + required = false, + fixed = true), + @TargetAttributeType( + name = "Attach to process by pid", + type = FridaModelTargetProcessAttachByPidConnectorImpl.class, + required = true, + fixed = true), + @TargetAttributeType(type = Void.class) + }, + canonicalContainer = true) +public class FridaModelTargetConnectorContainerImpl extends FridaModelTargetObjectImpl { + + protected final FridaModelTargetRoot root; + + private FridaModelTargetConnector defaultConnector; + + protected final FridaModelTargetProcessLaunchConnectorImpl processLauncher; + protected final FridaModelTargetProcessLaunchWithOptionsConnectorImpl processLauncherEx; + protected final FridaModelTargetProcessAttachByPidConnectorImpl processAttacherByPid; + + public FridaModelTargetConnectorContainerImpl(FridaModelTargetRoot root) { + super(root.getModel(), root, "Connectors", "ConnectorsContainer"); + this.root = root; + + this.processLauncher = + new FridaModelTargetProcessLaunchConnectorImpl(this, "Launch process"); + this.processLauncherEx = + new FridaModelTargetProcessLaunchWithOptionsConnectorImpl(this, "Launch process w/ options"); + this.processAttacherByPid = + new FridaModelTargetProcessAttachByPidConnectorImpl(this, "Attach to process by pid"); + this.defaultConnector = processLauncher; + + changeAttributes(List.of(), List.of( // + processAttacherByPid, // + processLauncher, // + processLauncherEx // + ), Map.of( // + DISPLAY_ATTRIBUTE_NAME, "Connectors" // + ), "Initialized"); + } + + public FridaModelTargetConnector getDefaultConnector() { + return defaultConnector; + } + + public void setDefaultConnector(FridaModelTargetConnector defaultConnector) { + this.defaultConnector = defaultConnector; + root.setDefaultConnector(defaultConnector); + } + +} diff --git a/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/impl/FridaModelTargetExportContainerImpl.java b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/impl/FridaModelTargetExportContainerImpl.java new file mode 100644 index 0000000000..a200e42b6f --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/impl/FridaModelTargetExportContainerImpl.java @@ -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.model.impl; + +import java.util.List; +import java.util.Map; +import java.util.concurrent.CompletableFuture; +import java.util.stream.Collectors; + +import agent.frida.manager.FridaExport; +import agent.frida.model.iface2.FridaModelTargetExportContainer; +import ghidra.dbg.target.TargetObject; +import ghidra.dbg.target.schema.TargetAttributeType; +import ghidra.dbg.target.schema.TargetElementType; +import ghidra.dbg.target.schema.TargetObjectSchema.ResyncMode; +import ghidra.dbg.target.schema.TargetObjectSchemaInfo; + +@TargetObjectSchemaInfo( + name = "ExportContainer", + elements = { + @TargetElementType(type = FridaModelTargetExportImpl.class) }, + elementResync = ResyncMode.ONCE, + attributes = { + @TargetAttributeType(type = Void.class) }, + canonicalContainer = true) +public class FridaModelTargetExportContainerImpl extends FridaModelTargetObjectImpl + implements FridaModelTargetExportContainer { + + protected final FridaModelTargetModuleImpl module; + + public FridaModelTargetExportContainerImpl(FridaModelTargetModuleImpl module) { + super(module.getModel(), module, "Exports", "ExportContainer"); + this.module = module; + } + + @Override + public CompletableFuture requestElements(boolean refresh) { + return getManager().listModuleExports(module.getModule()).thenAccept(byName -> { + List symbols; + synchronized (this) { + symbols = byName.values() + .stream() + .map(this::getTargetExport) + .collect(Collectors.toList()); + } + setElements(symbols, Map.of(), "Refreshed"); + }); + } + + @Override + public synchronized FridaModelTargetExportImpl getTargetExport(FridaExport symbol) { + TargetObject targetObject = getMapObject(symbol); + if (targetObject != null) { + FridaModelTargetExportImpl targetExport = (FridaModelTargetExportImpl) targetObject; + targetExport.setModelObject(symbol); + return targetExport; + } + return new FridaModelTargetExportImpl(this, symbol); + } +} diff --git a/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/impl/FridaModelTargetExportImpl.java b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/impl/FridaModelTargetExportImpl.java new file mode 100644 index 0000000000..566bd1b28f --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/impl/FridaModelTargetExportImpl.java @@ -0,0 +1,101 @@ +/* ### + * 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.model.impl; + +import java.util.List; +import java.util.Map; + +import agent.frida.manager.FridaExport; +import agent.frida.model.iface2.FridaModelTargetExport; +import agent.frida.model.methods.FridaModelTargetFunctionInterceptorImpl; +import agent.frida.model.methods.FridaModelTargetUnloadScriptImpl; +import ghidra.dbg.target.TargetObject; +import ghidra.dbg.target.TargetSymbol; +import ghidra.dbg.target.schema.TargetAttributeType; +import ghidra.dbg.target.schema.TargetElementType; +import ghidra.dbg.target.schema.TargetObjectSchemaInfo; +import ghidra.dbg.util.PathUtils; +import ghidra.program.model.address.Address; +import ghidra.program.model.address.AddressFormatException; + +@TargetObjectSchemaInfo( + name = "Export", + elements = { + @TargetElementType(type = Void.class) }, + attributes = { + @TargetAttributeType(name = TargetObject.VALUE_ATTRIBUTE_NAME, type = Address.class), + @TargetAttributeType(name = TargetSymbol.SIZE_ATTRIBUTE_NAME, type = long.class), + @TargetAttributeType(name = "Address", type = Address.class), + @TargetAttributeType(name = "Name", type = String.class), + @TargetAttributeType(name = "Type", type = String.class), + @TargetAttributeType(type = Object.class) }) +public class FridaModelTargetExportImpl extends FridaModelTargetObjectImpl + implements FridaModelTargetExport { + protected static String indexExport(FridaExport symbol) { + return symbol.getName(); + } + + protected static String keyExport(FridaExport symbol) { + return PathUtils.makeKey(indexExport(symbol)); + } + + protected final boolean constant; + protected Address value; + protected String name; + private FridaModelTargetFunctionInterceptorImpl intercept; + private FridaModelTargetUnloadScriptImpl unload; + + public FridaModelTargetExportImpl(FridaModelTargetExportContainerImpl exports, + FridaExport export) { + super(exports.getModel(), exports, keyExport(export), export, "Export"); + this.constant = false; + try { + this.value = exports.getModel() + .getAddressSpace("ram") + .getAddress(export.getAddress()); + } catch (AddressFormatException e) { + e.printStackTrace(); + } + this.name = export.getName(); + this.intercept = new FridaModelTargetFunctionInterceptorImpl(this); + this.unload = new FridaModelTargetUnloadScriptImpl(this, intercept.getName()); + + changeAttributes(List.of(), List.of(), Map.of( // + DISPLAY_ATTRIBUTE_NAME, getDescription(0), // + VALUE_ATTRIBUTE_NAME, value, // + "Address", value, // + "Name", export.getName(), // + "Type", export.getType(), // + intercept.getName(), intercept, // + unload.getName(), unload // + ), "Initialized"); + } + + public String getDescription(int level) { + FridaExport symbol = (FridaExport) getModelObject(); + return symbol.getName(); + } + + @Override + public Address getValue() { + return value; + } + + @Override + public String getName() { + return name; + } +} diff --git a/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/impl/FridaModelTargetFileSpecImpl.java b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/impl/FridaModelTargetFileSpecImpl.java new file mode 100644 index 0000000000..8e83a46f9c --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/impl/FridaModelTargetFileSpecImpl.java @@ -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.model.impl; + +import java.util.List; +import java.util.Map; + +import agent.frida.frida.FridaClient; +import agent.frida.manager.FridaFileSpec; +import agent.frida.model.iface2.FridaModelTargetFileSpec; +import agent.frida.model.iface2.FridaModelTargetMemoryRegion; +import ghidra.dbg.target.schema.TargetAttributeType; +import ghidra.dbg.target.schema.TargetObjectSchemaInfo; +import ghidra.dbg.util.PathUtils; + +@TargetObjectSchemaInfo( + name = "FileSpec", + attributes = { + @TargetAttributeType(type = Object.class) + }, + canonicalContainer = true) +public class FridaModelTargetFileSpecImpl extends FridaModelTargetObjectImpl + implements FridaModelTargetFileSpec { + + protected static String keyFileSpec(FridaFileSpec file) { + return PathUtils.makeKey(FridaClient.getId(file)); + } + + private FridaFileSpec fileSpec; + + public FridaModelTargetFileSpecImpl(FridaModelTargetMemoryRegion region, FridaFileSpec fileSpec) { + super(region.getModel(), region, "File", "FileSpec"); + this.fileSpec = fileSpec; + + if (fileSpec != null) { + changeAttributes(List.of(), List.of(), Map.of( // + DISPLAY_ATTRIBUTE_NAME, getDescription(), // + "Path", fileSpec.getPath(), // + "Offset", fileSpec.getOffset(), // + "Size", fileSpec.getSize() // + ), "Initialized"); + } + } + + public String getDescription() { + return fileSpec.getFilename(); + } + +} diff --git a/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/impl/FridaModelTargetFunctionImpl.java b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/impl/FridaModelTargetFunctionImpl.java new file mode 100644 index 0000000000..878acf9a85 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/impl/FridaModelTargetFunctionImpl.java @@ -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.model.impl; + +import java.util.List; +import java.util.Map; + +import agent.frida.frida.FridaClient; +import agent.frida.manager.FridaFunction; +import agent.frida.model.iface2.FridaModelTargetFunction; +import agent.frida.model.iface2.FridaModelTargetStackFrame; +import ghidra.dbg.target.schema.TargetAttributeType; +import ghidra.dbg.target.schema.TargetObjectSchemaInfo; +import ghidra.dbg.util.PathUtils; + +@TargetObjectSchemaInfo( + name = "Function", + attributes = { + @TargetAttributeType(type = Object.class) + }, + canonicalContainer = true) +public class FridaModelTargetFunctionImpl extends FridaModelTargetObjectImpl + implements FridaModelTargetFunction { + + protected static String keyFunction(FridaFunction fn) { + return PathUtils.makeKey(FridaClient.getId(fn)); + } + + protected final FridaModelTargetStackFrame frame; + + public FridaModelTargetFunctionImpl(FridaModelTargetStackFrame frame, FridaFunction function) { + super(frame.getModel(), frame, "Function", function, "Function"); + this.frame = frame; + + if (function != null) { + changeAttributes(List.of(), List.of(), Map.of( // + DISPLAY_ATTRIBUTE_NAME, getDescription(0), // + "Module Name", function.getModuleName(), // + "Function Name", function.getFunctionName(), // + "File Name", function.getFileName(), // + "Line Number", function.getLineNumber() // + ), "Initialized"); + } + } + + public String getDescription(int level) { + FridaFunction function = (FridaFunction) getModelObject(); + return function.getFunctionName(); + } + +} diff --git a/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/impl/FridaModelTargetHeapMemoryContainerImpl.java b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/impl/FridaModelTargetHeapMemoryContainerImpl.java new file mode 100644 index 0000000000..f8199881fd --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/impl/FridaModelTargetHeapMemoryContainerImpl.java @@ -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.model.impl; + +import java.util.concurrent.CompletableFuture; + +import agent.frida.model.iface2.FridaModelTargetProcess; +import ghidra.dbg.target.schema.*; +import ghidra.dbg.target.schema.TargetObjectSchema.ResyncMode; + +@TargetObjectSchemaInfo( + name = "Memory", + elementResync = ResyncMode.ALWAYS, + elements = { + @TargetElementType(type = FridaModelTargetMemoryRegionImpl.class) + }, + attributes = { + @TargetAttributeType(type = Void.class) + }, + canonicalContainer = true) +public class FridaModelTargetHeapMemoryContainerImpl extends FridaModelTargetMemoryContainerImpl { + + public FridaModelTargetHeapMemoryContainerImpl(FridaModelTargetProcess process) { + super(process, "Memory (Heap)"); + } + + @Override + public CompletableFuture requestElements(boolean refresh) { + return getManager().listHeapMemory(process.getProcess()); + } + +} diff --git a/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/impl/FridaModelTargetImportContainerImpl.java b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/impl/FridaModelTargetImportContainerImpl.java new file mode 100644 index 0000000000..2832a5e0d3 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/impl/FridaModelTargetImportContainerImpl.java @@ -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.model.impl; + +import java.util.List; +import java.util.Map; +import java.util.concurrent.CompletableFuture; +import java.util.stream.Collectors; + +import agent.frida.manager.FridaImport; +import agent.frida.model.iface2.FridaModelTargetImportContainer; +import ghidra.dbg.target.TargetObject; +import ghidra.dbg.target.schema.TargetAttributeType; +import ghidra.dbg.target.schema.TargetElementType; +import ghidra.dbg.target.schema.TargetObjectSchema.ResyncMode; +import ghidra.dbg.target.schema.TargetObjectSchemaInfo; + +@TargetObjectSchemaInfo( + name = "ImportContainer", + elements = { + @TargetElementType(type = FridaModelTargetImportImpl.class) }, + elementResync = ResyncMode.ONCE, + attributes = { + @TargetAttributeType(type = Void.class) }, + canonicalContainer = true) +public class FridaModelTargetImportContainerImpl extends FridaModelTargetObjectImpl + implements FridaModelTargetImportContainer { + + protected final FridaModelTargetModuleImpl module; + + public FridaModelTargetImportContainerImpl(FridaModelTargetModuleImpl module) { + super(module.getModel(), module, "Imports", "ImportContainer"); + this.module = module; + } + + @Override + public CompletableFuture requestElements(boolean refresh) { + return getManager().listModuleImports(module.getModule()).thenAccept(byName -> { + List symbols; + synchronized (this) { + symbols = byName.values() + .stream() + .map(this::getTargetImport) + .collect(Collectors.toList()); + } + setElements(symbols, Map.of(), "Refreshed"); + }); + } + + @Override + public synchronized FridaModelTargetImportImpl getTargetImport(FridaImport symbol) { + TargetObject targetObject = getMapObject(symbol); + if (targetObject != null) { + FridaModelTargetImportImpl targetImport = (FridaModelTargetImportImpl) targetObject; + targetImport.setModelObject(symbol); + return targetImport; + } + return new FridaModelTargetImportImpl(this, symbol); + } +} diff --git a/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/impl/FridaModelTargetImportImpl.java b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/impl/FridaModelTargetImportImpl.java new file mode 100644 index 0000000000..c5be8eae73 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/impl/FridaModelTargetImportImpl.java @@ -0,0 +1,113 @@ +/* ### + * 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.model.impl; + +import java.util.List; +import java.util.Map; + +import agent.frida.manager.FridaImport; +import agent.frida.model.iface2.FridaModelTargetImport; +import agent.frida.model.methods.FridaModelTargetFunctionInterceptorImpl; +import agent.frida.model.methods.FridaModelTargetUnloadScriptImpl; +import ghidra.dbg.target.TargetObject; +import ghidra.dbg.target.TargetSymbol; +import ghidra.dbg.target.schema.TargetAttributeType; +import ghidra.dbg.target.schema.TargetElementType; +import ghidra.dbg.target.schema.TargetObjectSchemaInfo; +import ghidra.dbg.util.PathUtils; +import ghidra.program.model.address.Address; +import ghidra.program.model.address.AddressFormatException; + +@TargetObjectSchemaInfo( + name = "Import", + elements = { + @TargetElementType(type = Void.class) }, + attributes = { + @TargetAttributeType(name = TargetObject.VALUE_ATTRIBUTE_NAME, type = Address.class), + @TargetAttributeType(name = TargetSymbol.SIZE_ATTRIBUTE_NAME, type = long.class), + @TargetAttributeType(name = "Name", type = String.class), + @TargetAttributeType(name = "Address", type = Address.class), + @TargetAttributeType(name = "Type", type = String.class), + @TargetAttributeType(name = "Slot", type = String.class), + @TargetAttributeType(type = Object.class) }) +public class FridaModelTargetImportImpl extends FridaModelTargetObjectImpl + implements FridaModelTargetImport { + protected static String indexImport(FridaImport symbol) { + return symbol.getName(); + } + + protected static String keyImport(FridaImport symbol) { + return PathUtils.makeKey(indexImport(symbol)); + } + + protected final boolean constant; + protected Address value; + protected long size; + protected String name; + private FridaModelTargetFunctionInterceptorImpl intercept; + private FridaModelTargetUnloadScriptImpl unload; + + public FridaModelTargetImportImpl(FridaModelTargetImportContainerImpl imports, + FridaImport imp) { + super(imports.getModel(), imports, keyImport(imp), imp, "Import"); + this.constant = false; + try { + this.value = imports.getModel() + .getAddressSpace("ram") + .getAddress(imp.getAddress()); + } catch (AddressFormatException e) { + e.printStackTrace(); + } + this.name = imp.getName(); + this.intercept = new FridaModelTargetFunctionInterceptorImpl(this); + this.unload = new FridaModelTargetUnloadScriptImpl(this, intercept.getName()); + + changeAttributes(List.of(), List.of(), Map.of( // + DISPLAY_ATTRIBUTE_NAME, getDescription(0), // + VALUE_ATTRIBUTE_NAME, value, // + "Address", value, // + "Name", imp.getName(), // + "Type", imp.getType(), // + "Slot", imp.getSlot(), // + intercept.getName(), intercept, // + unload.getName(), unload // + ), "Initialized"); + } + + public String getDescription(int level) { + FridaImport symbol = (FridaImport) getModelObject(); + return symbol.getName(); + } + + public boolean isConstant() { + return constant; + } + + @Override + public Address getValue() { + return value; + } + + @Override + public long getSize() { + return size; + } + + @Override + public String getName() { + return name; + } +} diff --git a/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/impl/FridaModelTargetKernelImpl.java b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/impl/FridaModelTargetKernelImpl.java new file mode 100644 index 0000000000..fcbb78a13b --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/impl/FridaModelTargetKernelImpl.java @@ -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.model.impl; + +import java.util.List; +import java.util.Map; + +import agent.frida.model.iface2.FridaModelTargetMemoryContainer; +import agent.frida.model.iface2.FridaModelTargetModuleContainer; +import ghidra.dbg.target.schema.TargetAttributeType; +import ghidra.dbg.target.schema.TargetObjectSchemaInfo; + +@TargetObjectSchemaInfo( + name = "DebugContainer", + attributes = { + @TargetAttributeType( + name = "Modules", + type = FridaModelTargetModuleContainerImpl.class, + required = true, + fixed = true), + @TargetAttributeType( + name = "Memory", + type = FridaModelTargetMemoryContainerImpl.class, + required = true, + fixed = true), + @TargetAttributeType(type = Object.class) + }, + canonicalContainer = true) +public class FridaModelTargetKernelImpl extends FridaModelTargetObjectImpl { + + protected final FridaModelTargetKernelMemoryContainerImpl memory; + protected final FridaModelTargetKernelModuleContainerImpl modules; + + public FridaModelTargetKernelImpl(FridaModelTargetSessionImpl session) { + super(session.getModel(), session, "Kernel", "Kernel"); + + this.memory = new FridaModelTargetKernelMemoryContainerImpl(this); + this.modules = new FridaModelTargetKernelModuleContainerImpl(this); + + changeAttributes(List.of(), List.of( // + memory, + modules // + ), Map.of( + "Base", session.getSession().getAttribute("kbase"), // + "PageSize", session.getSession().getAttribute("kPageSize") // + ), "Initialized"); + } + + public FridaModelTargetMemoryContainer getMemory() { + return memory; + } + + public FridaModelTargetModuleContainer getModules() { + return modules; + } + +} diff --git a/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/impl/FridaModelTargetKernelMemoryContainerImpl.java b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/impl/FridaModelTargetKernelMemoryContainerImpl.java new file mode 100644 index 0000000000..9c3611b6da --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/impl/FridaModelTargetKernelMemoryContainerImpl.java @@ -0,0 +1,201 @@ +/* ### + * 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.model.impl; + +import java.nio.ByteBuffer; +import java.util.*; +import java.util.concurrent.CompletableFuture; + +import com.google.common.collect.Range; +import com.google.common.collect.RangeSet; + +import agent.frida.frida.FridaRegionInfo; +import agent.frida.manager.*; +import agent.frida.manager.cmd.FridaReadKernelMemoryCommand; +import agent.frida.manager.cmd.FridaWriteKernelMemoryCommand; +import agent.frida.manager.impl.FridaManagerImpl; +import agent.frida.model.iface2.FridaModelTargetMemoryContainer; +import agent.frida.model.iface2.FridaModelTargetMemoryRegion; +import agent.frida.model.methods.*; +import ghidra.dbg.error.DebuggerMemoryAccessException; +import ghidra.dbg.error.DebuggerModelAccessException; +import ghidra.dbg.target.TargetObject; +import ghidra.dbg.target.schema.*; +import ghidra.dbg.target.schema.TargetObjectSchema.ResyncMode; +import ghidra.program.model.address.Address; +import ghidra.util.Msg; +import ghidra.util.datastruct.WeakValueHashMap; + +@TargetObjectSchemaInfo( + name = "Memory", + elementResync = ResyncMode.ALWAYS, + elements = { + @TargetElementType(type = FridaModelTargetMemoryRegionImpl.class) + }, + attributes = { + @TargetAttributeType(type = Object.class) + }, + canonicalContainer = true) +public class FridaModelTargetKernelMemoryContainerImpl extends FridaModelTargetObjectImpl + implements FridaModelTargetMemoryContainer { + + protected final FridaModelTargetKernelImpl kernel; + protected final FridaModelTargetMemoryScanImpl scan; + protected final FridaModelTargetMemoryProtectImpl prot; + protected final FridaModelTargetMemoryWatchImpl watch; + protected final FridaModelTargetUnloadScriptImpl unload; + + protected final Map memoryRegions = + new WeakValueHashMap<>(); + + + public FridaModelTargetKernelMemoryContainerImpl(FridaModelTargetKernelImpl kernel) { + super(kernel.getModel(), kernel, "Memory", "MemoryContainer"); + this.kernel = kernel; + + this.scan = new FridaModelTargetMemoryScanImpl(this, true); + this.prot = new FridaModelTargetMemoryProtectImpl(this, true); + this.watch = new FridaModelTargetMemoryWatchImpl(this); + this.unload = new FridaModelTargetUnloadScriptImpl(this, watch.getName()); + this.changeAttributes(List.of(), List.of(), Map.of( // + scan.getName(), scan, // + prot.getName(), prot, // + watch.getName(), watch, // + unload.getName(), unload // + ), "Initialized"); + + getManager().addEventsListener(this); + requestElements(false); + } + + @Override + public CompletableFuture requestElements(boolean refresh) { + if (refresh) { + listeners.fire.invalidateCacheRequested(this); + } + return getManager().listKernelMemory(); + } + + @Override + public synchronized FridaModelTargetMemoryRegion getTargetMemory(FridaMemoryRegionInfo region) { + TargetObject targetObject = getMapObject(region); + if (targetObject != null) { + FridaModelTargetMemoryRegion targetRegion = (FridaModelTargetMemoryRegion) targetObject; + targetRegion.setModelObject(region); + return targetRegion; + } + return new FridaModelTargetMemoryRegionImpl(this, region); + } + + private byte[] readAssist(Address address, ByteBuffer buf, long offset, RangeSet set) { + if (set == null) { + return new byte[0]; + } + Range range = set.rangeContaining(offset); + if (range == null) { + throw new DebuggerMemoryAccessException("Cannot read at " + address); + } + listeners.fire.memoryUpdated(getProxy(), address, buf.array()); + return Arrays.copyOf(buf.array(), (int) (range.upperEndpoint() - range.lowerEndpoint())); + } + + private void writeAssist(Address address, byte[] data) { + listeners.fire.memoryUpdated(getProxy(), address, data); + } + + @Override + public CompletableFuture readMemory(Address address, int length) { + return model.gateFuture(doReadMemory(address, length)); + } + + protected CompletableFuture doReadMemory(Address address, int length) { + FridaManagerImpl manager = getManager(); + if (manager.isWaiting()) { + throw new DebuggerModelAccessException( + "Cannot process command readMemory while engine is waiting for events"); + } + ByteBuffer buf = ByteBuffer.allocate(length); + long offset = address.getOffset(); + if (!manager.isKernelMode() || address.getAddressSpace().getName().equals("ram")) { + return manager + .execute(new FridaReadKernelMemoryCommand(manager, address, buf, buf.remaining())) + .thenApply(set -> { + return readAssist(address, buf, offset, set); + }); + } + return CompletableFuture.completedFuture(new byte[length]); + } + + @Override + public CompletableFuture writeMemory(Address address, byte[] data) { + return model.gateFuture(doWriteMemory(address, data)); + } + + protected CompletableFuture doWriteMemory(Address address, byte[] data) { + FridaManagerImpl manager = getManager(); + if (manager.isWaiting()) { + throw new DebuggerModelAccessException( + "Cannot process command writeMemory while engine is waiting for events"); + } + ByteBuffer buf = ByteBuffer.wrap(data); + if (!manager.isKernelMode() || address.getAddressSpace().getName().equals("ram")) { + return manager + .execute(new FridaWriteKernelMemoryCommand(manager, address, buf, buf.remaining())) + .thenAccept(___ -> { + writeAssist(address, data); + }); + } + return CompletableFuture.completedFuture(null); + } + + @Override + public void regionAdded(FridaProcess process, FridaRegionInfo info, int index, FridaCause cause) { + FridaModelTargetMemoryRegion targetRegion; + FridaMemoryRegionInfo region = info.getRegion(index); + synchronized (this) { + /** + * It's not a good idea to remove "stale" entries. If the entry's already present, it's + * probably because several modules were loaded at once, at it has already had its + * sections loaded. Removing it will cause it to load all module sections again! + */ + //modulesByName.remove(name); + targetRegion = getTargetMemory(region); + } + if (targetRegion == null) { + Msg.error(this, "Region " + region.getRangeAddress() + " not found!"); + return; + } + changeElements(List.of(), List.of(targetRegion), Map.of(), "Added"); + } + + @Override + public void regionReplaced(FridaProcess process, FridaRegionInfo info, int index, FridaCause cause) { + FridaMemoryRegionInfo region = info.getRegion(index); + changeElements(List.of(), List.of(getTargetMemory(region)), Map.of(), "Replaced"); + FridaModelTargetMemoryRegion targetRegion = getTargetMemory(region); + changeElements(List.of(), List.of(targetRegion), Map.of(), "Replaced"); + } + + @Override + public void regionRemoved(FridaProcess process, FridaRegionInfo info, int index, FridaCause cause) { + FridaModelTargetMemoryRegion targetRegion = getTargetMemory(info.getRegion(index)); + if (targetRegion != null) { + FridaModelImpl impl = (FridaModelImpl) model; + impl.deleteModelObject(targetRegion.getModelObject()); + } + changeElements(List.of(targetRegion.getName()), List.of(), Map.of(), "Removed"); + } +} diff --git a/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/impl/FridaModelTargetKernelModuleContainerImpl.java b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/impl/FridaModelTargetKernelModuleContainerImpl.java new file mode 100644 index 0000000000..6e60c25431 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/impl/FridaModelTargetKernelModuleContainerImpl.java @@ -0,0 +1,131 @@ +/* ### + * 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.model.impl; + +import java.util.List; +import java.util.Map; +import java.util.concurrent.CompletableFuture; + +import agent.frida.frida.FridaModuleInfo; +import agent.frida.manager.*; +import agent.frida.model.iface2.FridaModelTargetModule; +import agent.frida.model.iface2.FridaModelTargetModuleContainer; +import ghidra.dbg.target.*; +import ghidra.dbg.target.schema.*; +import ghidra.dbg.target.schema.TargetObjectSchema.ResyncMode; +import ghidra.util.Msg; + +@TargetObjectSchemaInfo( + name = "ModuleContainer", + elements = { // + @TargetElementType(type = FridaModelTargetModuleImpl.class) // + }, // + elementResync = ResyncMode.ONCE, // + attributes = { // + @TargetAttributeType(type = Object.class) // + }, + canonicalContainer = true) +public class FridaModelTargetKernelModuleContainerImpl extends FridaModelTargetObjectImpl + implements FridaModelTargetModuleContainer { + + protected final FridaModelTargetKernelImpl kernel; + + public FridaModelTargetKernelModuleContainerImpl(FridaModelTargetKernelImpl kernel) { + super(kernel.getModel(), kernel, "Modules", "ModuleContainer"); + this.kernel = kernel; + + getManager().addEventsListener(this); + requestElements(false); + } + + @Override + public void moduleLoaded(FridaProcess proc, FridaModuleInfo info, int index, FridaCause cause) { + FridaModelTargetModule targetModule; + FridaModule module = info.getModule(index); + synchronized (this) { + /** + * It's not a good idea to remove "stale" entries. If the entry's already present, it's + * probably because several modules were loaded at once, at it has already had its + * sections loaded. Removing it will cause it to load all module sections again! + */ + //modulesByName.remove(name); + targetModule = getTargetModule(module); + } + if (targetModule == null) { + Msg.error(this, "Module " + info.getModuleName(index) + " not found!"); + return; + } + FridaThread thread = getManager().getCurrentThread(); + TargetThread eventThread = + (TargetThread) getModel().getModelObject(thread); + changeElements(List.of(), List.of(targetModule), Map.of(), "Loaded"); + getListeners().fire.event(getProxy(), eventThread, TargetEventType.MODULE_LOADED, + "Library " + info.getModuleName(index) + " loaded", List.of(targetModule)); + } + + @Override + public void moduleReplaced(FridaProcess proc, FridaModuleInfo info, int index, FridaCause cause) { + FridaModule module = info.getModule(index); + changeElements(List.of(), List.of(getTargetModule(module)), Map.of(), "Replaced"); + FridaModelTargetModule targetModule = getTargetModule(module); + changeElements(List.of(), List.of(targetModule), Map.of(), "Replaced"); + } + + @Override + public void moduleUnloaded(FridaProcess proc, FridaModuleInfo info, int index, FridaCause cause) { + FridaModelTargetModule targetModule = getTargetModule(info.getModule(index)); + if (targetModule != null) { + FridaThread thread = getManager().getCurrentThread(); + TargetThread eventThread = + (TargetThread) getModel().getModelObject(thread); + getListeners().fire.event(getProxy(), eventThread, TargetEventType.MODULE_UNLOADED, + "Library " + info.getModuleName(index) + " unloaded", List.of(targetModule)); + FridaModelImpl impl = (FridaModelImpl) model; + impl.deleteModelObject(targetModule.getModule()); + } + changeElements(List.of(info.getModuleName(index)), List.of(), Map.of(), "Unloaded"); + } + + @Override + public boolean supportsSyntheticModules() { + return false; + } + + @Override + public CompletableFuture addSyntheticModule(String name) { + throw new UnsupportedOperationException("frida does not support synthetic modules"); + } + + @Override + public CompletableFuture requestElements(boolean refresh) { + if (refresh) { + listeners.fire.invalidateCacheRequested(this); + } + return getManager().listKernelModules(); + } + + @Override + public FridaModelTargetKernelModuleImpl getTargetModule(FridaModule module) { + TargetObject targetObject = getMapObject(module); + if (targetObject != null) { + FridaModelTargetKernelModuleImpl targetModule = (FridaModelTargetKernelModuleImpl) targetObject; + targetModule.setModelObject(module); + return targetModule; + } + return new FridaModelTargetKernelModuleImpl(this, (FridaKernelModule) module); + } + +} diff --git a/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/impl/FridaModelTargetKernelModuleImpl.java b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/impl/FridaModelTargetKernelModuleImpl.java new file mode 100644 index 0000000000..1551dbe5ef --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/impl/FridaModelTargetKernelModuleImpl.java @@ -0,0 +1,103 @@ +/* ### + * 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.model.impl; + +import java.util.List; +import java.util.Map; + +import agent.frida.frida.FridaClient; +import agent.frida.manager.*; +import agent.frida.model.iface2.FridaModelTargetModule; +import ghidra.dbg.target.schema.*; +import ghidra.dbg.util.PathUtils; +import ghidra.program.model.address.*; + +@TargetObjectSchemaInfo( + name = "Module", + elements = { + @TargetElementType(type = Void.class) + }, + attributes = { + @TargetAttributeType(name = "BaseAddress", type = Address.class), + @TargetAttributeType(name = "ImageName", type = String.class), + //@TargetAttributeType(name = "UUID", type = String.class), + @TargetAttributeType(name = "Len", type = String.class), + @TargetAttributeType(type = Void.class) + }) +public class FridaModelTargetKernelModuleImpl extends FridaModelTargetObjectImpl + implements FridaModelTargetModule { + + protected static String indexModule(FridaModule module) { + return FridaClient.getId(module); + } + + protected static String keyModule(FridaModule module) { + return PathUtils.makeKey(indexModule(module)); + } + + public FridaModelTargetKernelModuleImpl(FridaModelTargetKernelModuleContainerImpl modules, FridaKernelModule module) { + super(modules.getModel(), modules, keyModule(module), module, "Module"); + + AddressSpace space = getModel().getAddressSpace("ram"); + Address address; + changeAttributes(List.of(), List.of(), Map.of( // + //DISPLAY_ATTRIBUTE_NAME, getDescription(0), // + SHORT_DISPLAY_ATTRIBUTE_NAME, module.getName(), // + MODULE_NAME_ATTRIBUTE_NAME, module.getPath(), // + "ImageName", module.getPath() // + //"UUID", module.GetUUIDString() // + ), "Initialized"); + + try { + address = space.getAddress(module.getRangeAddress()); + AddressRangeImpl range = new AddressRangeImpl(address, module.getRangeSize()); + changeAttributes(List.of(), List.of(), Map.of( // + RANGE_ATTRIBUTE_NAME, range, // + "BaseAddress", address, // + "Len", Long.toHexString(module.getRangeSize()) // + ), "Initialized"); + } catch (AddressFormatException | AddressOverflowException e) { + // Nothing + } + + } + + public String getDescription(int level) { + FridaModule module = (FridaModule) getModelObject(); + return module.getName(); + } + + protected Address doGetBase() { + return null; //getModel().getAddressSpace("ram").getAddress(module.getKnownBase()); + } + + @Override + public FridaModule getModule() { + return (FridaModule) getModelObject(); + } + + @Override + public void setRange(AddressRangeImpl range) { + if (range != null) { + changeAttributes(List.of(), List.of(), Map.of( // + RANGE_ATTRIBUTE_NAME, range, // + "BaseAddress", range.getMinAddress(), // + "Len", Long.toHexString(range.getLength()) // + ), "Initialized"); + } + } + +} diff --git a/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/impl/FridaModelTargetMemoryContainerImpl.java b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/impl/FridaModelTargetMemoryContainerImpl.java new file mode 100644 index 0000000000..4a46b97ca7 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/impl/FridaModelTargetMemoryContainerImpl.java @@ -0,0 +1,235 @@ +/* ### + * 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.model.impl; + +import java.nio.ByteBuffer; +import java.util.Arrays; +import java.util.List; +import java.util.Map; +import java.util.concurrent.CompletableFuture; + +import com.google.common.collect.Range; +import com.google.common.collect.RangeSet; + +import agent.frida.frida.FridaRegionInfo; +import agent.frida.manager.FridaCause; +import agent.frida.manager.FridaMemoryRegionInfo; +import agent.frida.manager.FridaProcess; +import agent.frida.manager.cmd.FridaReadMemoryCommand; +import agent.frida.manager.cmd.FridaWriteMemoryCommand; +import agent.frida.manager.impl.FridaManagerImpl; +import agent.frida.model.iface2.FridaModelTargetMemoryContainer; +import agent.frida.model.iface2.FridaModelTargetMemoryRegion; +import agent.frida.model.iface2.FridaModelTargetProcess; +import agent.frida.model.methods.FridaModelTargetMemoryPatchImpl; +import agent.frida.model.methods.FridaModelTargetMemoryProtectImpl; +import agent.frida.model.methods.FridaModelTargetMemoryScanImpl; +import agent.frida.model.methods.FridaModelTargetMemoryWatchImpl; +import agent.frida.model.methods.FridaModelTargetUnloadScriptImpl; +import ghidra.dbg.error.DebuggerMemoryAccessException; +import ghidra.dbg.error.DebuggerModelAccessException; +import ghidra.dbg.target.TargetObject; +import ghidra.dbg.target.schema.TargetAttributeType; +import ghidra.dbg.target.schema.TargetElementType; +import ghidra.dbg.target.schema.TargetObjectSchema.ResyncMode; +import ghidra.dbg.target.schema.TargetObjectSchemaInfo; +import ghidra.program.model.address.Address; +import ghidra.util.Msg; +import ghidra.util.datastruct.WeakValueHashMap; + +@TargetObjectSchemaInfo( + name = "Memory", + elementResync = ResyncMode.ALWAYS, + elements = { + @TargetElementType(type = FridaModelTargetMemoryRegionImpl.class) + }, + attributes = { + @TargetAttributeType(type = Object.class) + }, + canonicalContainer = true) +public class FridaModelTargetMemoryContainerImpl extends FridaModelTargetObjectImpl + implements FridaModelTargetMemoryContainer { + + protected final FridaModelTargetProcess process; + protected final FridaModelTargetMemoryScanImpl scan; + protected final FridaModelTargetMemoryPatchImpl patch; + protected final FridaModelTargetMemoryProtectImpl prot; + protected final FridaModelTargetMemoryWatchImpl watch; + protected final FridaModelTargetUnloadScriptImpl unload; + + protected final Map memoryRegions = + new WeakValueHashMap<>(); + + + public FridaModelTargetMemoryContainerImpl(FridaModelTargetProcess process) { + super(process.getModel(), process, "Memory", "MemoryContainer"); + this.process = process; + + this.scan = new FridaModelTargetMemoryScanImpl(this, false); + this.patch = new FridaModelTargetMemoryPatchImpl(this); + this.prot = new FridaModelTargetMemoryProtectImpl(this, false); + this.watch = new FridaModelTargetMemoryWatchImpl(this); + this.unload = new FridaModelTargetUnloadScriptImpl(this, watch.getName()); + this.changeAttributes(List.of(), List.of(), Map.of( // + scan.getName(), scan, // + patch.getName(), patch, // + prot.getName(), prot, // + watch.getName(), watch, // + unload.getName(), unload // + ), "Initialized"); + + getManager().addEventsListener(this); + requestElements(true); + } + + public FridaModelTargetMemoryContainerImpl(FridaModelTargetProcess process, String name) { + super(process.getModel(), process, name, "MemoryContainer"); + this.process = process; + + this.scan = new FridaModelTargetMemoryScanImpl(this, false); + this.patch = new FridaModelTargetMemoryPatchImpl(this); + this.prot = new FridaModelTargetMemoryProtectImpl(this, false); + this.watch = new FridaModelTargetMemoryWatchImpl(this); + this.unload = new FridaModelTargetUnloadScriptImpl(this, watch.getName()); + this.changeAttributes(List.of(), List.of(), Map.of( // + scan.getName(), scan, // + patch.getName(), patch, // + prot.getName(), prot, // + watch.getName(), watch, // + unload.getName(), unload // + ), "Initialized"); + + requestElements(false); + } + + @Override + public CompletableFuture requestElements(boolean refresh) { + if (refresh) { + listeners.fire.invalidateCacheRequested(this); + } + return getManager().listMemory(process.getProcess()); + } + + @Override + public synchronized FridaModelTargetMemoryRegion getTargetMemory(FridaMemoryRegionInfo region) { + TargetObject targetObject = getMapObject(region); + if (targetObject != null) { + FridaModelTargetMemoryRegion targetRegion = (FridaModelTargetMemoryRegion) targetObject; + targetRegion.setModelObject(region); + return targetRegion; + } + return new FridaModelTargetMemoryRegionImpl(this, region); + } + + private byte[] readAssist(Address address, ByteBuffer buf, long offset, RangeSet set) { + if (set == null) { + return new byte[0]; + } + Range range = set.rangeContaining(offset); + if (range == null) { + throw new DebuggerMemoryAccessException("Cannot read at " + address); + } + listeners.fire.memoryUpdated(getProxy(), address, buf.array()); + return Arrays.copyOf(buf.array(), (int) (range.upperEndpoint() - range.lowerEndpoint())); + } + + private void writeAssist(Address address, byte[] data) { + listeners.fire.memoryUpdated(getProxy(), address, data); + } + + @Override + public CompletableFuture readMemory(Address address, int length) { + return model.gateFuture(doReadMemory(address, length)); + } + + protected CompletableFuture doReadMemory(Address address, int length) { + FridaManagerImpl manager = getManager(); + if (manager.isWaiting()) { + throw new DebuggerModelAccessException( + "Cannot process command readMemory while engine is waiting for events"); + } + ByteBuffer buf = ByteBuffer.allocate(length); + long offset = address.getOffset(); + if (!manager.isKernelMode() || address.getAddressSpace().getName().equals("ram")) { + return manager + .execute(new FridaReadMemoryCommand(manager, address, buf, buf.remaining())) + .thenApply(set -> { + return readAssist(address, buf, offset, set); + }); + } + return CompletableFuture.completedFuture(new byte[length]); + } + + @Override + public CompletableFuture writeMemory(Address address, byte[] data) { + return model.gateFuture(doWriteMemory(address, data)); + } + + protected CompletableFuture doWriteMemory(Address address, byte[] data) { + FridaManagerImpl manager = getManager(); + if (manager.isWaiting()) { + throw new DebuggerModelAccessException( + "Cannot process command writeMemory while engine is waiting for events"); + } + ByteBuffer buf = ByteBuffer.wrap(data); + if (!manager.isKernelMode() || address.getAddressSpace().getName().equals("ram")) { + return manager + .execute(new FridaWriteMemoryCommand(manager, address, buf, buf.remaining())) + .thenAccept(___ -> { + writeAssist(address, data); + }); + } + return CompletableFuture.completedFuture(null); + } + + @Override + public void regionAdded(FridaProcess proc, FridaRegionInfo info, int index, FridaCause cause) { + FridaModelTargetMemoryRegion targetRegion; + FridaMemoryRegionInfo region = info.getRegion(index); + synchronized (this) { + /** + * It's not a good idea to remove "stale" entries. If the entry's already present, it's + * probably because several modules were loaded at once, at it has already had its + * sections loaded. Removing it will cause it to load all module sections again! + */ + //modulesByName.remove(name); + targetRegion = getTargetMemory(region); + } + if (targetRegion == null) { + Msg.error(this, "Region " + region.getRangeAddress() + " not found!"); + return; + } + changeElements(List.of(), List.of(targetRegion), Map.of(), "Added"); + } + + @Override + public void regionReplaced(FridaProcess proc, FridaRegionInfo info, int index, FridaCause cause) { + FridaMemoryRegionInfo region = info.getRegion(index); + changeElements(List.of(), List.of(getTargetMemory(region)), Map.of(), "Replaced"); + FridaModelTargetMemoryRegion targetRegion = getTargetMemory(region); + changeElements(List.of(), List.of(targetRegion), Map.of(), "Replaced"); + } + + @Override + public void regionRemoved(FridaProcess proc, FridaRegionInfo info, int index, FridaCause cause) { + FridaModelTargetMemoryRegion targetRegion = getTargetMemory(info.getRegion(index)); + if (targetRegion != null) { + FridaModelImpl impl = (FridaModelImpl) model; + impl.deleteModelObject(targetRegion.getModelObject()); + } + changeElements(List.of(targetRegion.getName()), List.of(), Map.of(), "Removed"); + } +} diff --git a/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/impl/FridaModelTargetMemoryRegionImpl.java b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/impl/FridaModelTargetMemoryRegionImpl.java new file mode 100644 index 0000000000..da20931675 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/impl/FridaModelTargetMemoryRegionImpl.java @@ -0,0 +1,136 @@ +/* ### + * 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.model.impl; + +import java.util.List; +import java.util.Map; + +import agent.frida.manager.FridaMemoryRegionInfo; +import agent.frida.model.iface2.FridaModelTargetMemoryContainer; +import agent.frida.model.iface2.FridaModelTargetMemoryRegion; +import ghidra.dbg.target.TargetMemoryRegion; +import ghidra.dbg.target.schema.TargetAttributeType; +import ghidra.dbg.target.schema.TargetElementType; +import ghidra.dbg.target.schema.TargetObjectSchemaInfo; +import ghidra.dbg.util.PathUtils; +import ghidra.program.model.address.Address; +import ghidra.program.model.address.AddressFormatException; +import ghidra.program.model.address.AddressRange; +import ghidra.program.model.address.AddressRangeImpl; +import ghidra.program.model.address.AddressSpace; + +@TargetObjectSchemaInfo( + name = "MemoryRegion", + elements = { + @TargetElementType(type = Void.class) }, + attributes = { + @TargetAttributeType( + name = TargetMemoryRegion.MEMORY_ATTRIBUTE_NAME, + type = FridaModelTargetMemoryContainerImpl.class), + @TargetAttributeType( + name = "File", + type = FridaModelTargetFileSpecImpl.class), + @TargetAttributeType(name = "RegionBase", type = Address.class), + @TargetAttributeType(name = "RegionEnd", type = Address.class), + @TargetAttributeType(name = "RegionSize", type = String.class), + @TargetAttributeType(name = "Protection", type = String.class), + @TargetAttributeType(type = Object.class) }) +public class FridaModelTargetMemoryRegionImpl extends FridaModelTargetObjectImpl + implements FridaModelTargetMemoryRegion { + + protected static String keySection(FridaMemoryRegionInfo region) { + return PathUtils.makeKey(region.getRangeAddress()); + } + + protected AddressRange range; + protected List protect; + protected List allocProtect; + private boolean isRead; + private boolean isWrite; + private boolean isExec; + private FridaModelTargetFileSpecImpl fileSpec; + + public FridaModelTargetMemoryRegionImpl(FridaModelTargetMemoryContainer memory, + FridaMemoryRegionInfo region) { + super(memory.getModel(), memory, keySection(region), region, "Region"); + + this.isRead = region.isReadable(); + this.isWrite = region.isWritable(); + this.isExec = region.isExecutable(); + this.changeAttributes(List.of(), List.of(), Map.of( // + MEMORY_ATTRIBUTE_NAME, memory, // + READABLE_ATTRIBUTE_NAME, isRead, // + WRITABLE_ATTRIBUTE_NAME, isWrite, // + EXECUTABLE_ATTRIBUTE_NAME, isExec, // + "Protection", region.getProtection() // + ), "Initialized"); + + range = doGetRange(region); + if (range != null) { + this.changeAttributes(List.of(), List.of(), Map.of( // + RANGE_ATTRIBUTE_NAME, range, // + "RegionBase", range.getMinAddress(), // + "RegionEnd", range.getMaxAddress(), // + "RegionSize", Long.toHexString(range.getMaxAddress().subtract(range.getMinAddress()) + 1) // + ), "Initialized"); + } + + if (region.getFileSpec() != null) { + this.fileSpec = new FridaModelTargetFileSpecImpl(this, region.getFileSpec()); + this.changeAttributes(List.of(), List.of(), Map.of( // + "File", fileSpec // + ), "Initialized"); + } + + } + + protected AddressRange doGetRange(FridaMemoryRegionInfo s) { + try { + AddressSpace addressSpace = getModel().getAddressSpace("ram"); + Address min = addressSpace.getAddress(s.getRangeAddress()); + Address max = min.add(s.getRangeSize() - 1); + return max.getOffset() > min.getOffset() ? new AddressRangeImpl(min, max) + : new AddressRangeImpl(min, min); + } catch (AddressFormatException e) { + return null; + } + } + + @Override + public AddressRange getRange() { + return range; + } + + @Override + public boolean isReadable() { + return isRead; + } + + @Override + public boolean isWritable() { + return isWrite; + } + + @Override + public boolean isExecutable() { + return isExec; + } + + public boolean isSame(FridaMemoryRegionInfo section) { + return range.equals(doGetRange(section)); + } + +} diff --git a/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/impl/FridaModelTargetModuleContainerImpl.java b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/impl/FridaModelTargetModuleContainerImpl.java new file mode 100644 index 0000000000..42f76d2b39 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/impl/FridaModelTargetModuleContainerImpl.java @@ -0,0 +1,162 @@ +/* ### + * 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.model.impl; + +import java.util.List; +import java.util.Map; +import java.util.concurrent.CompletableFuture; + +import agent.frida.frida.FridaModuleInfo; +import agent.frida.manager.FridaCause; +import agent.frida.manager.FridaModule; +import agent.frida.manager.FridaProcess; +import agent.frida.manager.FridaSession; +import agent.frida.manager.FridaThread; +import agent.frida.model.iface2.FridaModelTargetModule; +import agent.frida.model.iface2.FridaModelTargetModuleContainer; +import agent.frida.model.iface2.FridaModelTargetSession; +import agent.frida.model.methods.FridaModelTargetModuleInitImpl; +import agent.frida.model.methods.FridaModelTargetModuleInterceptorImpl; +import agent.frida.model.methods.FridaModelTargetModuleLoadImpl; +import agent.frida.model.methods.FridaModelTargetUnloadScriptImpl; +import ghidra.dbg.target.TargetModule; +import ghidra.dbg.target.TargetObject; +import ghidra.dbg.target.TargetThread; +import ghidra.dbg.target.schema.TargetAttributeType; +import ghidra.dbg.target.schema.TargetElementType; +import ghidra.dbg.target.schema.TargetObjectSchema.ResyncMode; +import ghidra.dbg.target.schema.TargetObjectSchemaInfo; +import ghidra.util.Msg; + +@TargetObjectSchemaInfo( + name = "ModuleContainer", + elements = { // + @TargetElementType(type = FridaModelTargetModuleImpl.class) // + }, // + elementResync = ResyncMode.ONCE, // + attributes = { // + @TargetAttributeType(type = Object.class) // + }, + canonicalContainer = true) +public class FridaModelTargetModuleContainerImpl extends FridaModelTargetObjectImpl + implements FridaModelTargetModuleContainer { + + protected final FridaModelTargetSession targetSession; + protected final FridaSession session; + private FridaModelTargetModuleLoadImpl load; + private FridaModelTargetModuleInitImpl init; + private FridaModelTargetModuleInterceptorImpl intercept; + private FridaModelTargetUnloadScriptImpl unload; + + public FridaModelTargetModuleContainerImpl(FridaModelTargetSession session) { + super(session.getModel(), session, "Modules", "ModuleContainer"); + this.targetSession = session; + this.session = (FridaSession) session.getModelObject(); + + this.load = new FridaModelTargetModuleLoadImpl(this); + this.init = new FridaModelTargetModuleInitImpl(this); + this.intercept = new FridaModelTargetModuleInterceptorImpl(this); + this.unload = new FridaModelTargetUnloadScriptImpl(this, intercept.getName()); + this.changeAttributes(List.of(), List.of( // + load, // + init, // + intercept, // + unload // + ), Map.of( // + ), "Initialized"); + + getManager().addEventsListener(this); + requestElements(true); + } + + @Override + public void moduleLoaded(FridaProcess proc, FridaModuleInfo info, int index, FridaCause cause) { + FridaModelTargetModule targetModule; + FridaModule module = info.getModule(index); + synchronized (this) { + /** + * It's not a good idea to remove "stale" entries. If the entry's already present, it's + * probably because several modules were loaded at once, at it has already had its + * sections loaded. Removing it will cause it to load all module sections again! + */ + //modulesByName.remove(name); + targetModule = getTargetModule(module); + } + if (targetModule == null) { + Msg.error(this, "Module " + info.getModuleName(index) + " not found!"); + return; + } + FridaThread thread = getManager().getCurrentThread(); + TargetThread eventThread = + (TargetThread) getModel().getModelObject(thread); + changeElements(List.of(), List.of(targetModule), Map.of(), "Loaded"); + getListeners().fire.event(getProxy(), eventThread, TargetEventType.MODULE_LOADED, + "Library " + info.getModuleName(index) + " loaded", List.of(targetModule)); + } + + @Override + public void moduleReplaced(FridaProcess proc, FridaModuleInfo info, int index, FridaCause cause) { + FridaModule module = info.getModule(index); + changeElements(List.of(), List.of(getTargetModule(module)), Map.of(), "Replaced"); + FridaModelTargetModule targetModule = getTargetModule(module); + changeElements(List.of(), List.of(targetModule), Map.of(), "Replaced"); + } + + @Override + public void moduleUnloaded(FridaProcess proc, FridaModuleInfo info, int index, FridaCause cause) { + FridaModelTargetModule targetModule = getTargetModule(info.getModule(index)); + if (targetModule != null) { + FridaThread thread = getManager().getCurrentThread(); + TargetThread eventThread = + (TargetThread) getModel().getModelObject(thread); + getListeners().fire.event(getProxy(), eventThread, TargetEventType.MODULE_UNLOADED, + "Library " + info.getModuleName(index) + " unloaded", List.of(targetModule)); + FridaModelImpl impl = (FridaModelImpl) model; + impl.deleteModelObject(targetModule.getModule()); + } + changeElements(List.of(info.getModuleName(index)), List.of(), Map.of(), "Unloaded"); + } + + @Override + public boolean supportsSyntheticModules() { + return false; + } + + @Override + public CompletableFuture addSyntheticModule(String name) { + throw new UnsupportedOperationException("frida does not support synthetic modules"); + } + + @Override + public CompletableFuture requestElements(boolean refresh) { + if (refresh) { + listeners.fire.invalidateCacheRequested(this); + } + return getManager().listModules(session.getProcess()); + } + + @Override + public FridaModelTargetModule getTargetModule(FridaModule module) { + TargetObject targetObject = getMapObject(module); + if (targetObject != null) { + FridaModelTargetModule targetModule = (FridaModelTargetModule) targetObject; + targetModule.setModelObject(module); + return targetModule; + } + return new FridaModelTargetModuleImpl(this, module); + } + +} diff --git a/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/impl/FridaModelTargetModuleImpl.java b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/impl/FridaModelTargetModuleImpl.java new file mode 100644 index 0000000000..02b7769738 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/impl/FridaModelTargetModuleImpl.java @@ -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.model.impl; + +import java.util.List; +import java.util.Map; + +import agent.frida.frida.FridaClient; +import agent.frida.manager.FridaModule; +import agent.frida.manager.FridaSession; +import agent.frida.model.iface2.FridaModelTargetModule; +import ghidra.dbg.target.schema.*; +import ghidra.dbg.util.PathUtils; +import ghidra.program.model.address.*; + +@TargetObjectSchemaInfo( + name = "Module", + elements = { + @TargetElementType(type = Void.class) + }, + attributes = { + @TargetAttributeType( + name = "Sections", + type = FridaModelTargetModuleSectionContainerImpl.class, + required = true, + fixed = true), + @TargetAttributeType( + name = "Symbols", + type = FridaModelTargetSymbolContainerImpl.class, + required = true, + fixed = true), + @TargetAttributeType( + name = "Imports", + type = FridaModelTargetImportContainerImpl.class, + required = true, + fixed = true), + @TargetAttributeType( + name = "Exports", + type = FridaModelTargetExportContainerImpl.class, + required = true, + fixed = true), + @TargetAttributeType(name = "BaseAddress", type = Address.class), + @TargetAttributeType(name = "ImageName", type = String.class), + //@TargetAttributeType(name = "UUID", type = String.class), + @TargetAttributeType(name = "Len", type = String.class), + @TargetAttributeType(type = Void.class) + }) +public class FridaModelTargetModuleImpl extends FridaModelTargetObjectImpl + implements FridaModelTargetModule { + + protected static String indexModule(FridaModule module) { + return FridaClient.getId(module); + } + + protected static String keyModule(FridaModule module) { + return PathUtils.makeKey(indexModule(module)); + } + + protected final FridaSession session; + + protected final FridaModelTargetSymbolContainerImpl symbols; + protected final FridaModelTargetImportContainerImpl imports; + protected final FridaModelTargetExportContainerImpl exports; + protected final FridaModelTargetModuleSectionContainerImpl sections; + + public FridaModelTargetModuleImpl(FridaModelTargetModuleContainerImpl modules, FridaModule module) { + super(modules.getModel(), modules, keyModule(module), module, "Module"); + this.session = modules.session; + + this.symbols = new FridaModelTargetSymbolContainerImpl(this); + this.imports = new FridaModelTargetImportContainerImpl(this); + this.exports = new FridaModelTargetExportContainerImpl(this); + this.sections = new FridaModelTargetModuleSectionContainerImpl(this); + + AddressSpace space = getModel().getAddressSpace("ram"); + Address address; + changeAttributes(List.of(), List.of( // + sections, // + symbols, // + imports, // + exports // + ), Map.of( // + //DISPLAY_ATTRIBUTE_NAME, getDescription(0), // + SHORT_DISPLAY_ATTRIBUTE_NAME, module.getName(), // + MODULE_NAME_ATTRIBUTE_NAME, module.getPath(), // + "ImageName", module.getPath() // + //"UUID", module.GetUUIDString() // + ), "Initialized"); + + try { + address = space.getAddress(module.getRangeAddress()); + AddressRangeImpl range = new AddressRangeImpl(address, module.getRangeSize()); + changeAttributes(List.of(), List.of(), Map.of( // + RANGE_ATTRIBUTE_NAME, range, // + "BaseAddress", address, // + "Len", Long.toHexString(module.getRangeSize()) // + ), "Initialized"); + } catch (AddressFormatException | AddressOverflowException e) { + // Nothing + } + + } + + public String getDescription(int level) { + FridaModule module = (FridaModule) getModelObject(); + return module.getName(); + } + + protected Address doGetBase() { + return null; //getModel().getAddressSpace("ram").getAddress(module.getKnownBase()); + } + + @Override + public FridaModule getModule() { + return (FridaModule) getModelObject(); + } + + public FridaSession getSession() { + return session; + } + + @Override + public void setRange(AddressRangeImpl range) { + if (range != null) { + changeAttributes(List.of(), List.of(), Map.of( // + RANGE_ATTRIBUTE_NAME, range, // + "BaseAddress", range.getMinAddress(), // + "Len", Long.toHexString(range.getLength()) // + ), "Initialized"); + } + } + +} diff --git a/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/impl/FridaModelTargetModuleSectionContainerImpl.java b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/impl/FridaModelTargetModuleSectionContainerImpl.java new file mode 100644 index 0000000000..ce210d0711 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/impl/FridaModelTargetModuleSectionContainerImpl.java @@ -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. + */ +package agent.frida.model.impl; + +import java.util.List; +import java.util.Map; +import java.util.concurrent.CompletableFuture; +import java.util.stream.Collectors; + +import agent.frida.manager.FridaModule; +import agent.frida.manager.FridaSection; +import agent.frida.model.iface2.FridaModelTargetModule; +import agent.frida.model.iface2.FridaModelTargetModuleSection; +import agent.frida.model.iface2.FridaModelTargetModuleSectionContainer; +import ghidra.dbg.target.TargetObject; +import ghidra.dbg.target.schema.TargetAttributeType; +import ghidra.dbg.target.schema.TargetElementType; +import ghidra.dbg.target.schema.TargetObjectSchemaInfo; +import ghidra.program.model.address.Address; +import ghidra.program.model.address.AddressRangeImpl; + +@TargetObjectSchemaInfo( + name = "SectionContainer", + elements = { + @TargetElementType(type = FridaModelTargetModuleSectionImpl.class) }, + attributes = { + @TargetAttributeType(type = Void.class) }, + canonicalContainer = true) +public class FridaModelTargetModuleSectionContainerImpl extends FridaModelTargetObjectImpl + implements FridaModelTargetModuleSectionContainer { + + protected final FridaModelTargetModule module; + + public FridaModelTargetModuleSectionContainerImpl(FridaModelTargetModule module) { + super(module.getModel(), module, "Sections", "ModuleSections"); + this.module = module; + requestElements(false); + } + + @Override + public CompletableFuture requestElements(boolean refresh) { + return getManager().listModuleSections(module.getModule()).thenAccept(byStart -> { + List sections; + synchronized (this) { + sections = byStart.values() + .stream() + .map(this::getModuleSection) + .collect(Collectors.toList()); + setElements(sections, "Refreshed"); + updateRange(); + } + }); + } + + protected synchronized FridaModelTargetModuleSection getModuleSection(FridaSection section) { + TargetObject targetObject = getMapObject(section); + if (targetObject != null) { + FridaModelTargetModuleSection targetSection = + (FridaModelTargetModuleSection) targetObject; + targetSection.setModelObject(section); + return targetSection; + } + return new FridaModelTargetModuleSectionImpl(this, section); + } + + public void updateRange() { + Map els = getCachedElements(); + Address min = null; + Address max = null; + for (TargetObject element : els.values()) { + FridaModelTargetModuleSectionImpl section = (FridaModelTargetModuleSectionImpl) element; + Address start = section.getStart(); + if (start.getOffset() > 0) { + if (min == null || min.getOffset() > start.getOffset()) { + min = start; + } + } + Address stop = section.getEnd(); + if (stop.getOffset() > 0) { + if (max == null || max.getOffset() < stop.getOffset()) { + max = stop; + } + } + } + if (min != null & max != null) { + module.setRange(new AddressRangeImpl(min, max)); + } + } + + public FridaModule getModule() { + return module.getModule(); + } + +} diff --git a/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/impl/FridaModelTargetModuleSectionImpl.java b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/impl/FridaModelTargetModuleSectionImpl.java new file mode 100644 index 0000000000..5061e6c47d --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/impl/FridaModelTargetModuleSectionImpl.java @@ -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.model.impl; + +import java.util.List; +import java.util.Map; + +import agent.frida.manager.FridaSection; +import agent.frida.model.iface2.FridaModelTargetModuleSection; +import ghidra.dbg.target.schema.TargetAttributeType; +import ghidra.dbg.target.schema.TargetElementType; +import ghidra.dbg.target.schema.TargetObjectSchemaInfo; +import ghidra.dbg.util.PathUtils; +import ghidra.program.model.address.Address; +import ghidra.program.model.address.AddressRange; +import ghidra.program.model.address.AddressRangeImpl; +import ghidra.program.model.address.AddressSpace; + +@TargetObjectSchemaInfo( + name = "Section", + elements = { + @TargetElementType(type = Void.class) }, + attributes = { + @TargetAttributeType(type = Object.class) }) +public class FridaModelTargetModuleSectionImpl extends FridaModelTargetObjectImpl + implements FridaModelTargetModuleSection { + + protected static String keySection(FridaSection section) { + return PathUtils.makeKey(section.getRangeAddress()); + } + + protected static final String OBJFILE_ATTRIBUTE_NAME = PREFIX_INVISIBLE + "objfile"; + + protected AddressRange range; + + public FridaModelTargetModuleSectionImpl(FridaModelTargetModuleSectionContainerImpl sections, + FridaSection section) { + super(sections.getModel(), sections, keySection(section), section, "Section"); + + AddressSpace space = getModel().getAddressSpace("ram"); + Address min = space.getAddress(0L); + long lval = Long.decode(section.getRangeAddress()); + if (lval != -1) { + min = space.getAddress(lval); + } + // Ghidra ranges are not inclusive at the end. + long sz = section.getRangeSize().longValue(); + Address max = min.add(sz); + range = new AddressRangeImpl(min, max); + if (range != null) { + changeAttributes(List.of(), List.of(), Map.of( // + RANGE_ATTRIBUTE_NAME, range // + ), "Initialized"); + } + + changeAttributes(List.of(), List.of(), Map.of( // + MODULE_ATTRIBUTE_NAME, sections.getParent(), // + DISPLAY_ATTRIBUTE_NAME, getDescription(2), // + "Address", min, // + "File Offset", section.getFileOffset(), // + "Size", Long.toHexString(sz), // + "Permissions", section.getProtection() // + ), "Initialized"); + } + + public String getDescription(int level) { + FridaSection section = (FridaSection) getModelObject(); + String description = section.getRangeAddress(); + if (level > 0) { + description += " " + section.getProtection(); + } + if (level > 1) { + description += " " + Long.toHexString(section.getFileOffset()); + } + if (level > 2) { + description += " " + section.getFilePath(); + } + return description; + } + + @Override + public AddressRange getRange() { + return range; + } + +} diff --git a/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/impl/FridaModelTargetObjectImpl.java b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/impl/FridaModelTargetObjectImpl.java new file mode 100644 index 0000000000..ada514d632 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/impl/FridaModelTargetObjectImpl.java @@ -0,0 +1,264 @@ +/* ### + * 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.model.impl; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.CompletableFuture; + +import agent.frida.frida.FridaClient; +import agent.frida.manager.FridaCause; +import agent.frida.manager.FridaStateListener; +import agent.frida.manager.FridaState; +import agent.frida.model.AbstractFridaModel; +import agent.frida.model.iface1.FridaModelTargetAccessConditioned; +import agent.frida.model.iface1.FridaModelTargetExecutionStateful; +import agent.frida.model.iface2.FridaModelTargetObject; +import agent.frida.model.iface2.FridaModelTargetProcess; +import agent.frida.model.iface2.FridaModelTargetSession; +import agent.frida.model.iface2.FridaModelTargetThread; +import ghidra.dbg.agent.DefaultTargetObject; +import ghidra.dbg.target.TargetAccessConditioned; +import ghidra.dbg.target.TargetExecutionStateful.TargetExecutionState; +import ghidra.dbg.target.TargetObject; +import ghidra.dbg.target.TargetProcess; +import ghidra.dbg.target.TargetThread; +import ghidra.dbg.target.schema.TargetObjectSchema; + +public class FridaModelTargetObjectImpl extends DefaultTargetObject + implements FridaModelTargetObject { + + protected boolean accessible = true; + protected final FridaStateListener accessListener = this::checkExited; + private boolean modified; + + private Object modelObject; + protected Map objectMap = new HashMap<>(); + + public FridaModelTargetObjectImpl(AbstractFridaModel impl, TargetObject parent, String name, + String typeHint) { + super(impl, parent, name, typeHint); + this.setModelObject(((FridaModelTargetObject) parent).getModelObject()); + getManager().addStateListener(accessListener); + } + + public FridaModelTargetObjectImpl(AbstractFridaModel impl, TargetObject parent, String name, + Object modelObject, + String typeHint) { + super(impl, parent, name, typeHint); + //((FridaModelTargetObject) parent).addMapObject(modelObject, this); + this.setModelObject(modelObject); + getManager().addStateListener(accessListener); + } + + public FridaModelTargetObjectImpl(AbstractFridaModel impl, TargetObject parent, String name, + Object modelObject, + String typeHint, TargetObjectSchema schema) { + super(impl, parent, name, typeHint, schema); + this.setModelObject(modelObject); + getManager().addStateListener(accessListener); + } + + public void setAttribute(String key, String value) { + changeAttributes(List.of(), List.of(), Map.of( // + key, value), "Initialized"); + } + + @Override + protected void doInvalidate(TargetObject branch, String reason) { + super.doInvalidate(branch, reason); + getManager().removeStateListener(accessListener); + } + + public void setAccessible(boolean accessible) { + synchronized (attributes) { + if (this.accessible == accessible) { + return; + } + this.accessible = accessible; + } + if (this instanceof FridaModelTargetAccessConditioned) { + changeAttributes(List.of(), List.of(), Map.of( // + TargetAccessConditioned.ACCESSIBLE_ATTRIBUTE_NAME, accessible // + ), "Accessibility changed"); + } + } + + @Override + public AbstractFridaModel getModel() { + return (AbstractFridaModel) model; + } + + public void onRunning() { + setAccessible(false); + } + + public void onStopped() { + setAccessible(true); + } + + public void onExit() { + setAccessible(true); + } + + protected void checkExited(FridaState state, FridaCause cause) { + TargetExecutionState exec = TargetExecutionState.INACTIVE; + switch (state) { + case FRIDA_THREAD_WAITING: + { + exec = TargetExecutionState.INACTIVE; + break; + } + case FRIDA_THREAD_UNINTERRUPTIBLE: + { + exec = TargetExecutionState.ALIVE; + break; + } + case FRIDA_THREAD_STOPPED: { + exec = TargetExecutionState.STOPPED; + onStopped(); + break; + } + case FRIDA_THREAD_RUNNING: + { + exec = TargetExecutionState.RUNNING; + resetModified(); + onRunning(); + break; + } + case FRIDA_THREAD_HALTED: { + exec = TargetExecutionState.TERMINATED; + if (getParentProcess() != null || this instanceof TargetProcess) { + getManager().removeStateListener(accessListener); + } + onExit(); + break; + } + } + if (this instanceof FridaModelTargetExecutionStateful) { + FridaModelTargetExecutionStateful stateful = (FridaModelTargetExecutionStateful) this; + stateful.setExecutionState(exec, "Refreshed"); + } + } + + @Override + public CompletableFuture> requestNativeAttributes() { + throw new AssertionError(); // shouldn't ever be here + } + + @Override + public CompletableFuture> requestNativeElements() { + throw new AssertionError(); // shouldn't ever be here + } + + @Override + public FridaModelTargetSession getParentSession() { + FridaModelTargetObject test = (FridaModelTargetObject) parent; + while (test != null && !(test instanceof FridaModelTargetSession)) { + test = (FridaModelTargetObject) test.getParent(); + } + return test == null ? null : (FridaModelTargetSession) test; + } + + @Override + public FridaModelTargetProcess getParentProcess() { + FridaModelTargetObject test = (FridaModelTargetObject) parent; + while (test != null && !(test instanceof TargetProcess)) { + test = (FridaModelTargetObject) test.getParent(); + } + return test == null ? null : (FridaModelTargetProcess) test; + } + + @Override + public FridaModelTargetThread getParentThread() { + FridaModelTargetObject test = (FridaModelTargetObject) parent; + while (test != null && !(test instanceof TargetThread)) { + test = (FridaModelTargetObject) test.getParent(); + } + return test == null ? null : (FridaModelTargetThread) test; + } + + @Override + public void setModified(Map map, boolean b) { + if (modified) { + map.put(MODIFIED_ATTRIBUTE_NAME, modified); + } + } + + @Override + public void setModified(boolean modified) { + if (modified) { + changeAttributes(List.of(), List.of(), Map.of( // + MODIFIED_ATTRIBUTE_NAME, modified // + ), "Refreshed"); + } + } + + @Override + public void resetModified() { + changeAttributes(List.of(), List.of(), Map.of( // + MODIFIED_ATTRIBUTE_NAME, false // + ), "Refreshed"); + } + + public TargetObject searchForSuitable(Class type) { + List pathToClass = model.getRootSchema().searchForSuitable(type, path); + return model.getModelObject(pathToClass); + } + + public String getDescription(int level) { + return getName(); + } + + @Override + public Object getModelObject() { + return modelObject; + } + + @Override + public void setModelObject(Object modelObject) { + if (modelObject != null) { + ((FridaModelTargetObject) parent).addMapObject(modelObject, this); + } + this.modelObject = modelObject; + } + + @Override + public void addMapObject(Object object, TargetObject targetObject) { + if (object == null) { + return; + } + objectMap.put(FridaClient.getModelKey(object), targetObject); + } + + @Override + public TargetObject getMapObject(Object object) { + if (object == null) { + return null; + } + return objectMap.get(FridaClient.getModelKey(object)); + } + + @Override + public void deleteMapObject(Object object) { + if (object == null) { + return; + } + objectMap.remove(FridaClient.getModelKey(object)); + } + +} diff --git a/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/impl/FridaModelTargetProcessAttachByPidConnectorImpl.java b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/impl/FridaModelTargetProcessAttachByPidConnectorImpl.java new file mode 100644 index 0000000000..e791468e23 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/impl/FridaModelTargetProcessAttachByPidConnectorImpl.java @@ -0,0 +1,86 @@ +/* ### + * 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.model.impl; + +import java.util.*; +import java.util.concurrent.CompletableFuture; + +import agent.frida.model.iface2.FridaModelTargetConnector; +import ghidra.async.AsyncUtils; +import ghidra.async.TypeSpec; +import ghidra.dbg.error.DebuggerUserException; +import ghidra.dbg.target.TargetMethod; +import ghidra.dbg.target.TargetMethod.ParameterDescription; +import ghidra.dbg.target.TargetMethod.TargetParameterMap; +import ghidra.dbg.target.schema.*; + +@TargetObjectSchemaInfo( + name = "ProcessAttachByPidConnector", + elements = { + @TargetElementType(type = Void.class) + }, + attributes = { + @TargetAttributeType(type = Void.class) + }) +public class FridaModelTargetProcessAttachByPidConnectorImpl extends FridaModelTargetObjectImpl + implements FridaModelTargetConnector { + + protected final FridaModelTargetConnectorContainerImpl connectors; + protected final TargetParameterMap paramDescs; + + public FridaModelTargetProcessAttachByPidConnectorImpl( + FridaModelTargetConnectorContainerImpl connectors, + String name) { + super(connectors.getModel(), connectors, name, name); + this.connectors = connectors; + + changeAttributes(List.of(), List.of(), Map.of( // + DISPLAY_ATTRIBUTE_NAME, getDisplay(), // + TargetMethod.PARAMETERS_ATTRIBUTE_NAME, + paramDescs = TargetParameterMap.copyOf(computeParameters()) // + ), "Initialized"); + } + + @Override + public CompletableFuture setActive() { + connectors.setDefaultConnector(this); + return CompletableFuture.completedFuture(null); + } + + protected Map> computeParameters() { + HashMap> map = + new HashMap>(); + ParameterDescription param = ParameterDescription.create(String.class, "Pid", true, + "", "Pid", "process id for the target process"); + map.put("Pid", param); + return map; + } + + @Override + public TargetParameterMap getParameters() { + return TargetMethod.getParameters(this); + } + + @Override + public CompletableFuture launch(Map args) { + String pidstr = (String) args.get("Pid"); + return AsyncUtils.sequence(TypeSpec.VOID).then(seq -> { + getManager().attach(pidstr).handle(seq::nextIgnore); + }).finish().exceptionally((exc) -> { + throw new DebuggerUserException("Launch failed for " + args); + }); + } +} diff --git a/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/impl/FridaModelTargetProcessContainerImpl.java b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/impl/FridaModelTargetProcessContainerImpl.java new file mode 100644 index 0000000000..7d27935c7e --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/impl/FridaModelTargetProcessContainerImpl.java @@ -0,0 +1,149 @@ +/* ### + * 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.model.impl; + +import java.util.List; +import java.util.Map; +import java.util.concurrent.CompletableFuture; +import java.util.stream.Collectors; + +import agent.frida.frida.*; +import agent.frida.manager.*; +import agent.frida.model.iface1.FridaModelTargetConfigurable; +import agent.frida.model.iface2.*; +import ghidra.async.AsyncUtils; +import ghidra.dbg.error.DebuggerIllegalArgumentException; +import ghidra.dbg.target.TargetConfigurable; +import ghidra.dbg.target.TargetObject; +import ghidra.dbg.target.schema.*; + +@TargetObjectSchemaInfo( + name = "ProcessContainer", + elements = { // + @TargetElementType(type = FridaModelTargetProcessImpl.class) // + }, + attributes = { // + @TargetAttributeType(name = TargetConfigurable.BASE_ATTRIBUTE_NAME, type = Integer.class), // + @TargetAttributeType(type = Void.class) // + }, + canonicalContainer = true) +public class FridaModelTargetProcessContainerImpl extends FridaModelTargetObjectImpl + implements FridaModelTargetProcessContainer, FridaModelTargetConfigurable { + + private FridaModelTargetSessionImpl session; + + public FridaModelTargetProcessContainerImpl(FridaModelTargetSessionImpl session) { + super(session.getModel(), session, "Processes", "ProcessContainer"); + this.session = session; + this.changeAttributes(List.of(), Map.of(BASE_ATTRIBUTE_NAME, 16), "Initialized"); + + getManager().addEventsListener(this); + } + + @Override + public void processAdded(FridaProcess proc, FridaCause cause) { + FridaSession parentSession = (FridaSession) session.getModelObject(); + FridaSession procSession = proc.getSession(); + if (!FridaClient.getId(parentSession).equals(FridaClient.getId(procSession))) { + return; + } + session.setAccessible(true); + FridaModelTargetProcess process = getTargetProcess(proc); + changeElements(List.of(), List.of(process), Map.of(), "Added"); + process.processStarted(proc); + getListeners().fire.event(getProxy(), null, TargetEventType.PROCESS_CREATED, + "Process " + FridaClient.getId(proc) + " started " + process.getName(), + List.of(process)); + } + + @Override + public void processReplaced(FridaProcess proc, FridaCause cause) { + session.setAccessible(true); + FridaModelTargetProcess process = getTargetProcess(proc); + changeElements(List.of(), List.of(process), Map.of(), "Added"); + } + + @Override + public void processStarted(FridaProcess proc, FridaCause cause) { + FridaModelTargetProcess process = getTargetProcess(proc); + process.processStarted(proc); + } + + @Override + public void processRemoved(String processId, FridaCause cause) { + changeElements(List.of( // + processId // + ), List.of(), Map.of(), "Removed"); + } + + /* + @Override + public void threadStateChanged(FridaThread thread, FridaState state, FridaCause cause, + FridaReason reason) { + FridaModelTargetProcess process = getTargetProcess(thread.getProcess()); + process.threadStateChanged(thread, state, cause, reason); + } + */ + + @Override + public CompletableFuture requestElements(boolean refresh) { + return getManager().listProcesses(session.getSession()).thenAccept(byIID -> { + List processes; + synchronized (this) { + processes = byIID.values() + .stream() + .map(this::getTargetProcess) + .collect(Collectors.toList()); + } + setElements(processes, Map.of(), "Refreshed"); + }); + } + + @Override + public synchronized FridaModelTargetProcess getTargetProcess(FridaProcess process) { + TargetObject targetObject = getMapObject(process); + if (targetObject != null) { + FridaModelTargetProcess targetProcess = (FridaModelTargetProcess) targetObject; + targetProcess.setModelObject(process); + return targetProcess; + } + return new FridaModelTargetProcessImpl(this, process); + } + + @Override + public CompletableFuture writeConfigurationOption(String key, Object value) { + switch (key) { + case BASE_ATTRIBUTE_NAME: + if (value instanceof Integer) { + this.changeAttributes(List.of(), Map.of(BASE_ATTRIBUTE_NAME, value), + "Modified"); + for (TargetObject child : getCachedElements().values()) { + if (child instanceof FridaModelTargetProcessImpl) { + FridaModelTargetProcessImpl targetProcess = + (FridaModelTargetProcessImpl) child; + targetProcess.setBase(value); + } + } + } + else { + throw new DebuggerIllegalArgumentException("Base should be numeric"); + } + default: + } + return AsyncUtils.NIL; + } + +} diff --git a/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/impl/FridaModelTargetProcessImpl.java b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/impl/FridaModelTargetProcessImpl.java new file mode 100644 index 0000000000..3c496fbc7c --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/impl/FridaModelTargetProcessImpl.java @@ -0,0 +1,269 @@ +/* ### + * 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.model.impl; + +import java.util.List; +import java.util.Map; +import java.util.concurrent.CompletableFuture; + +import agent.frida.frida.FridaClient; +import agent.frida.manager.*; +import agent.frida.manager.cmd.*; +import agent.frida.model.iface1.FridaModelTargetFocusScope; +import agent.frida.model.iface2.*; +import ghidra.async.AsyncUtils; +import ghidra.dbg.target.*; +import ghidra.dbg.target.TargetEventScope.TargetEventType; +import ghidra.dbg.target.schema.*; +import ghidra.dbg.target.schema.TargetObjectSchema.ResyncMode; +import ghidra.dbg.util.PathUtils; + +@TargetObjectSchemaInfo( + name = "Process", + attributeResync = ResyncMode.ALWAYS, + elements = { + @TargetElementType(type = Void.class) }, + attributes = { + @TargetAttributeType( + name = "Memory", + type = FridaModelTargetMemoryContainerImpl.class, + required = true, + fixed = true), + @TargetAttributeType( + name = "Memory (Heap)", + type = FridaModelTargetMemoryContainerImpl.class, + required = false, + fixed = true), + @TargetAttributeType( + name = "Threads", + type = FridaModelTargetThreadContainerImpl.class, + required = true, + fixed = true), + @TargetAttributeType( + name = FridaModelTargetProcessImpl.EXIT_CODE_ATTRIBUTE_NAME, + type = String.class), + @TargetAttributeType(type = Void.class) }) +public class FridaModelTargetProcessImpl extends FridaModelTargetObjectImpl + implements FridaModelTargetProcess { + + public static final String PID_ATTRIBUTE_NAME = PREFIX_INVISIBLE + "pid"; + public static final String EXIT_CODE_ATTRIBUTE_NAME = PREFIX_INVISIBLE + "exit_code"; + + public static final TargetAttachKindSet SUPPORTED_KINDS = TargetAttachKindSet.of( // + TargetAttachKind.BY_OBJECT_REF, TargetAttachKind.BY_ID); + + protected static String indexProcess(FridaProcess process) { + return FridaClient.getId(process); + } + + protected static String keyProcess(FridaProcess process) { + return PathUtils.makeKey(indexProcess(process)); + } + + protected final FridaModelTargetMemoryContainerImpl memory; + protected final FridaModelTargetHeapMemoryContainerImpl heap; + protected final FridaModelTargetThreadContainerImpl threads; + + private Integer base = 10; + + public FridaModelTargetProcessImpl(FridaModelTargetProcessContainer processes, + FridaProcess process) { + super(processes.getModel(), processes, keyProcess(process), process, "Process"); + getModel().addModelObject(process, this); + + this.memory = new FridaModelTargetMemoryContainerImpl(this); + this.heap = new FridaModelTargetHeapMemoryContainerImpl(this); + this.threads = new FridaModelTargetThreadContainerImpl(this); + TargetExecutionState state = FridaClient.convertState(getManager().getState()); + + changeAttributes(List.of(), List.of( // + memory, // + heap, // + threads // + ), Map.of( // + //ACCESSIBLE_ATTRIBUTE_NAME, accessible = false, // + DISPLAY_ATTRIBUTE_NAME, getDisplay(), // + TargetMethod.PARAMETERS_ATTRIBUTE_NAME, PARAMETERS, // + STATE_ATTRIBUTE_NAME, state, // + SUPPORTED_ATTACH_KINDS_ATTRIBUTE_NAME, SUPPORTED_KINDS // + //SUPPORTED_STEP_KINDS_ATTRIBUTE_NAME, FridaModelTargetThreadImpl.SUPPORTED_KINDS // + ), "Initialized"); + setExecutionState(state, "Initialized"); + + getManager().addEventsListener(this); + } + + @Override + public void setModelObject(Object modelObject) { + super.setModelObject(modelObject); + getModel().addModelObject(modelObject, this); + } + + @Override + public String getDescription(int level) { + FridaProcess process = (FridaProcess) getModelObject(); + return process.getDescription(); + } + + @Override + public String getDisplay() { + String pidstr = FridaClient.getId(getProcess()); + if (base == 16) { + pidstr = "0x" + pidstr; + } + else { + pidstr = Long.toString(Long.parseLong(pidstr, 16)); + } + return "[" + pidstr + "]"; + } + + @Override + public void processSelected(FridaProcess eventProcess, FridaCause cause) { + if (eventProcess.getPID().equals(getProcess().getPID())) { + ((FridaModelTargetFocusScope) searchForSuitable(TargetFocusScope.class)).setFocus(this); + } + } + + @Override + public void threadStateChanged(FridaThread thread, FridaState state, FridaCause cause, + FridaReason reason) { + TargetExecutionState targetState = FridaClient.convertState(state); + setExecutionState(targetState, "ThreadStateChanged"); + if (state.equals(FridaState.FRIDA_THREAD_STOPPED)) { + threads.requestElements(true); + } + } + + @Override + public CompletableFuture launch(List args) { + model.gateFuture(getManager().execute(new FridaLaunchProcessCommand(getManager(), + getProcess().getName(), args))); + return AsyncUtils.NIL; + } + + @Override + public CompletableFuture resume() { + return model.gateFuture( + getManager().execute(new FridaContinueCommand(getManager(), getProcess()))); + } + + @Override + public CompletableFuture kill() { + return model.gateFuture(getManager().execute(new FridaKillCommand(getManager()))); + } + + @Override + public CompletableFuture destroy() { + return model.gateFuture(getManager().execute(new FridaDestroyCommand(getManager()))); + } + + @Override + public CompletableFuture attach(TargetAttachable attachable) { + getModel().assertMine(TargetObject.class, attachable); + // NOTE: Get the object and type check it myself. + // The typed ref could have been unsafely cast + FridaProcess proc = (FridaProcess) getModelObject(); + long pid = proc.getPID().longValue(); + return model.gateFuture( + getManager().execute(new FridaAttachCommand(getManager(), Long.toString(pid))) + .thenApply(__ -> null)); + } + + @Override + public CompletableFuture attach(long pid) { + return model.gateFuture( + getManager().execute(new FridaAttachCommand(getManager(), Long.toString(pid))) + .thenApply(__ -> null)); + } + + @Override + public CompletableFuture detach() { + return model.gateFuture( + getManager().execute(new FridaDetachCommand(getManager(), getProcess().getSession()))); + } + + @Override + public CompletableFuture delete() { + return AsyncUtils.NIL; + //return model.gateFuture(process.remove()); + } + + /* + @Override + public CompletableFuture step(TargetStepKind kind) { + return getManager().execute(new FridaStepCommand(getManager(), null, kind, null)); + } + + @Override + public CompletableFuture step(Map args) { + return getManager().execute(new FridaStepCommand(getManager(), null, null, args)); + } + */ + + @Override + public void processStarted(FridaProcess proc) { + if (proc != null) { + changeAttributes(List.of(), List.of(), Map.of( // + PID_ATTRIBUTE_NAME, getProcess().getPID().longValue(), // + DISPLAY_ATTRIBUTE_NAME, getDisplay(), // + STATE_ATTRIBUTE_NAME, TargetExecutionState.STOPPED // + ), "Started"); + } + setExecutionState(TargetExecutionState.STOPPED, "Started"); + } + + @Override + public void processExited(FridaProcess proc, FridaCause cause) { + if (proc.getPID().equals(this.getProcess().getPID())) { + String exitDesc = "NONE"; + changeAttributes(List.of(), List.of(), Map.of( // + STATE_ATTRIBUTE_NAME, TargetExecutionState.TERMINATED, // + EXIT_CODE_ATTRIBUTE_NAME, exitDesc // + ), "Exited"); + getListeners().fire.event(getProxy(), null, TargetEventType.PROCESS_EXITED, + "Process " + FridaClient.getId(getProcess()) + " exited code=" + exitDesc, + List.of(getProxy())); + } + } + + @Override + public CompletableFuture setActive() { + return CompletableFuture.completedFuture(null); + } + + @Override + public FridaModelTargetThreadContainer getThreads() { + return threads; + } + + @Override + public FridaModelTargetMemoryContainer getMemory() { + return memory; + } + + @Override + public FridaProcess getProcess() { + return (FridaProcess) getModelObject(); + } + + public void setBase(Object value) { + this.base = (Integer) value; + changeAttributes(List.of(), List.of(), Map.of( // + DISPLAY_ATTRIBUTE_NAME, getDisplay()// + ), "Started"); + } + +} diff --git a/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/impl/FridaModelTargetProcessLaunchConnectorImpl.java b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/impl/FridaModelTargetProcessLaunchConnectorImpl.java new file mode 100644 index 0000000000..2da3dad8f8 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/impl/FridaModelTargetProcessLaunchConnectorImpl.java @@ -0,0 +1,97 @@ +/* ### + * 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.model.impl; + +import java.util.*; +import java.util.concurrent.CompletableFuture; + +import agent.frida.model.iface2.FridaModelTargetConnector; +import ghidra.async.AsyncUtils; +import ghidra.async.TypeSpec; +import ghidra.dbg.error.DebuggerUserException; +import ghidra.dbg.target.TargetMethod; +import ghidra.dbg.target.TargetMethod.ParameterDescription; +import ghidra.dbg.target.TargetMethod.TargetParameterMap; +import ghidra.dbg.target.schema.TargetAttributeType; +import ghidra.dbg.target.schema.TargetElementType; +import ghidra.dbg.target.schema.TargetObjectSchemaInfo; + +@TargetObjectSchemaInfo( + name = "ProcessLaunchConnector", + elements = { // + @TargetElementType(type = Void.class) // + }, + attributes = { // + @TargetAttributeType(type = Void.class) // + }) +public class FridaModelTargetProcessLaunchConnectorImpl extends FridaModelTargetObjectImpl + implements FridaModelTargetConnector { + + protected final FridaModelTargetConnectorContainerImpl connectors; + protected final TargetParameterMap paramDescs; + + public FridaModelTargetProcessLaunchConnectorImpl( + FridaModelTargetConnectorContainerImpl connectors, + String name) { + super(connectors.getModel(), connectors, name, name); + this.connectors = connectors; + + changeAttributes(List.of(), List.of(), Map.of( // + DISPLAY_ATTRIBUTE_NAME, getDisplay(), // + TargetMethod.PARAMETERS_ATTRIBUTE_NAME, + paramDescs = TargetParameterMap.copyOf(computeParameters()) // + ), "Initialized"); + } + + @Override + public CompletableFuture setActive() { + connectors.setDefaultConnector(this); + return CompletableFuture.completedFuture(null); + } + + protected Map> computeParameters() { + HashMap> map = + new HashMap>(); + ParameterDescription param = ParameterDescription.create(String.class, "args", true, + "", "Command Line", "space-separated command-line arguments"); + map.put("args", param); + return map; + } + + @Override + public TargetParameterMap getParameters() { + return TargetMethod.getParameters(this); + } + + @Override + public CompletableFuture launch(Map args) { + return launch( + CmdLineParser.tokenize(TargetCmdLineLauncher.PARAMETER_CMDLINE_ARGS.get(args))); + } + + public CompletableFuture launch(List args) { + String fileName = args.get(0); + List nargs = new ArrayList<>(); + for (int i = 1; i < args.size(); i++) { + nargs.add(args.get(i)); + } + return AsyncUtils.sequence(TypeSpec.VOID).then(seq -> { + getManager().launch(fileName, nargs).handle(seq::nextIgnore); + }).finish().exceptionally((exc) -> { + throw new DebuggerUserException("Launch failed for " + args); + }); + } +} diff --git a/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/impl/FridaModelTargetProcessLaunchWithOptionsConnectorImpl.java b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/impl/FridaModelTargetProcessLaunchWithOptionsConnectorImpl.java new file mode 100644 index 0000000000..d9e459341f --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/impl/FridaModelTargetProcessLaunchWithOptionsConnectorImpl.java @@ -0,0 +1,160 @@ +/* ### + * 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.model.impl; + +import java.util.*; +import java.util.concurrent.CompletableFuture; + +import agent.frida.model.iface2.FridaModelTargetConnector; +import ghidra.async.AsyncUtils; +import ghidra.async.TypeSpec; +import ghidra.dbg.error.DebuggerUserException; +import ghidra.dbg.target.TargetMethod; +import ghidra.dbg.target.TargetMethod.ParameterDescription; +import ghidra.dbg.target.TargetMethod.TargetParameterMap; +import ghidra.dbg.target.schema.*; + +@TargetObjectSchemaInfo( + name = "ProcessLaunchWithOptionsConnector", + elements = { // + @TargetElementType(type = Void.class) // + }, + attributes = { // + @TargetAttributeType(type = Void.class) // + }) +public class FridaModelTargetProcessLaunchWithOptionsConnectorImpl extends FridaModelTargetObjectImpl + implements FridaModelTargetConnector { + + protected final FridaModelTargetConnectorContainerImpl connectors; + protected final TargetParameterMap paramDescs; + + public FridaModelTargetProcessLaunchWithOptionsConnectorImpl( + FridaModelTargetConnectorContainerImpl connectors, + String name) { + super(connectors.getModel(), connectors, name, name); + this.connectors = connectors; + + changeAttributes(List.of(), List.of(), Map.of( // + DISPLAY_ATTRIBUTE_NAME, getDisplay(), // + TargetMethod.PARAMETERS_ATTRIBUTE_NAME, + paramDescs = TargetParameterMap.copyOf(computeParameters()) // + ), "Initialized"); + } + + @Override + public CompletableFuture setActive() { + connectors.setDefaultConnector(this); + return CompletableFuture.completedFuture(null); + } + + protected Map> computeParameters() { + HashMap> map = + new LinkedHashMap>(); + ParameterDescription p0 = ParameterDescription.create(String.class, "File", true, + "", "File", "executable to be launched"); + map.put("File", p0); + ParameterDescription p1 = ParameterDescription.create(String.class, "Args", false, + "", "Args", "command-line arguments"); + map.put("Args", p1); + ParameterDescription p2 = ParameterDescription.create(String.class, "Env", false, + "", "Env", "environment arguments"); + map.put("Env", p2); + ParameterDescription p3 = ParameterDescription.create(String.class, "STDIN", false, + "", "STDIN", "path for STDIN"); + map.put("STDIN", p3); + ParameterDescription p4 = ParameterDescription.create(String.class, "STDOUT", false, + "", "STDOUT", "path for STDOUT"); + map.put("STDOUT", p4); + ParameterDescription p5 = ParameterDescription.create(String.class, "STDERR", false, + "", "STDERR", "path for STDERR"); + map.put("STDERR", p5); + ParameterDescription p6 = ParameterDescription.create(String.class, "Dir", false, + "", "Dir", "working directory"); + map.put("Dir", p6); + + ParameterDescription pF0 = ParameterDescription.create(Boolean.class, "Exec", + false, + false, "Exec", "exec when launching and turn the calling process into a new process"); + map.put("Exec", pF0); + ParameterDescription pF1 = + ParameterDescription.create(Boolean.class, "BreakOnLaunch", false, + true, "Break on launch", + "stop as soon as the process launches to allow the process to be debugged"); + map.put("BreakOnLaunch", pF1); + ParameterDescription pF2 = ParameterDescription.create(Boolean.class, + "BreakOnEntry", false, + true, "Break on entry", "stop at the program entry point instead of auto-continuing"); + map.put("BreakOnEntry", pF2); + ParameterDescription pF3 = + ParameterDescription.create(Boolean.class, "ASLR", false, + false, "Disable ASLR", "disable Address Space Layout Randomization (ASLR)"); + map.put("ASLR", pF3); + ParameterDescription pF4 = + ParameterDescription.create(Boolean.class, "STDIO", false, + false, "Disable STDIO", "disable stdio for inferior process (e.g. for a GUI app)"); + map.put("STDIO", pF4); + ParameterDescription pF5 = + ParameterDescription.create(Boolean.class, "NewTTY", false, + false, "New TTY", "launch the process in a new TTY if supported by the host"); + map.put("NewTTY", pF5); + ParameterDescription pF6 = ParameterDescription.create(Boolean.class, "Shell", + false, + false, "Launch from shell", "launch the process inside a shell to get shell expansion"); + map.put("Shell", pF6); + ParameterDescription pF7 = ParameterDescription.create(Boolean.class, "NewGroup", + false, + false, "New group", + "launch the process in a separate process group if you are going to hand the process off (e.g. to debugserver)"); + map.put("NewGroup", pF7); + ParameterDescription pF8 = + ParameterDescription.create(Boolean.class, "ExitRace", false, + false, "Suppress race on exit", + "set this flag so frida & the handee don’t race to set its exit status"); + map.put("ExitRace", pF8); + ParameterDescription pF9 = ParameterDescription.create(Boolean.class, "Detach", + false, + false, "Detach on disconnect", + "client stub should detach rather than killing the debugee if it loses connection with frida"); + map.put("Detach", pF9); + ParameterDescription pFA = + ParameterDescription.create(Boolean.class, "ExpandArgs", false, + false, "Shell-style expansion", "perform shell-style argument expansion"); + map.put("ExpandArgs", pFA); + ParameterDescription pFB = + ParameterDescription.create(Boolean.class, "CloseTTY", false, + false, "Close TTY on exit", "close the open TTY on exit"); + map.put("CloseTTY", pFB); + ParameterDescription pFC = + ParameterDescription.create(Boolean.class, "Inherit", false, + false, "Inherit TCC", "inherit TCC permissions from the parent"); + map.put("Inherit", pFC); + return map; + } + + @Override + public TargetParameterMap getParameters() { + return TargetMethod.getParameters(this); + } + + @Override + public CompletableFuture launch(Map args) { + return AsyncUtils.sequence(TypeSpec.VOID).then(seq -> { + getManager().launch(args).handle(seq::nextIgnore); + }).finish().exceptionally((exc) -> { + throw new DebuggerUserException("Launch failed for " + args); + }); + } +} diff --git a/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/impl/FridaModelTargetRegisterContainerImpl.java b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/impl/FridaModelTargetRegisterContainerImpl.java new file mode 100644 index 0000000000..1f152da536 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/impl/FridaModelTargetRegisterContainerImpl.java @@ -0,0 +1,165 @@ +/* ### + * 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.model.impl; + +import java.math.BigInteger; +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.concurrent.CompletableFuture; +import java.util.stream.Collectors; + +import agent.frida.manager.FridaReason; +import agent.frida.manager.FridaState; +import agent.frida.manager.FridaValue; +import agent.frida.model.iface2.FridaModelTargetRegister; +import agent.frida.model.iface2.FridaModelTargetRegisterBank; +import agent.frida.model.iface2.FridaModelTargetRegisterContainerAndBank; +import ghidra.async.AsyncUtils; +import ghidra.dbg.DebuggerModelListener; +import ghidra.dbg.error.DebuggerRegisterAccessException; +import ghidra.dbg.target.TargetObject; +import ghidra.dbg.target.TargetRegisterBank; +import ghidra.dbg.target.schema.TargetAttributeType; +import ghidra.dbg.target.schema.TargetElementType; +import ghidra.dbg.target.schema.TargetObjectSchema.ResyncMode; +import ghidra.dbg.target.schema.TargetObjectSchemaInfo; +import ghidra.util.datastruct.ListenerSet; + +@TargetObjectSchemaInfo( + name = "RegisterContainer", + attributeResync = ResyncMode.ALWAYS, + elements = { // + @TargetElementType(type = FridaModelTargetRegisterImpl.class) // + }, + attributes = { + @TargetAttributeType( + name = TargetRegisterBank.DESCRIPTIONS_ATTRIBUTE_NAME, + type = FridaModelTargetRegisterContainerImpl.class), + @TargetAttributeType(type = Void.class) + }, + canonicalContainer = true) +public class FridaModelTargetRegisterContainerImpl + extends FridaModelTargetObjectImpl + implements FridaModelTargetRegisterContainerAndBank { + public static final String NAME = "Registers"; + + protected final FridaModelTargetThreadImpl thread; + + public FridaModelTargetRegisterContainerImpl(FridaModelTargetThreadImpl thread) { + super(thread.getModel(), thread, NAME, "RegisterContainer"); + this.thread = thread; + + changeAttributes(List.of(), List.of(), Map.of( + DISPLAY_ATTRIBUTE_NAME, getName(), + DESCRIPTIONS_ATTRIBUTE_NAME, this), "Initialized"); + + requestElements(false); + } + + /** + * Does both descriptions and then populates values + */ + @Override + public CompletableFuture requestElements(boolean refresh) { + if (refresh) { + listeners.fire.invalidateCacheRequested(this); + } + return getManager().listRegisters(thread.getThread()).thenAccept(registers -> { + List targetRegisters; + synchronized (this) { + targetRegisters = registers.entrySet() + .stream() + .map(this::getTargetRegister) + .collect(Collectors.toList()); + } + setElements(targetRegisters, Map.of(), "Refreshed"); + if (!getCachedElements().isEmpty()) { + readRegistersNamed(getCachedElements().keySet()); + } + //changeAttributes(List.of(), targetRegisters, Map.of(), "Refreshed"); + }); + } + + @SuppressWarnings("rawtypes") + @Override + public FridaModelTargetRegister getTargetRegister(Entry entry) { + FridaValue val = new FridaValue((String) entry.getKey(), (String) entry.getValue()); + TargetObject targetObject = getMapObject(val); + if (targetObject != null) { + FridaModelTargetRegister targetRegister = (FridaModelTargetRegister) targetObject; + targetRegister.setModelObject(val); + return targetRegister; + } + return new FridaModelTargetRegisterImpl(this, val); + } + + public void threadStateChangedSpecific(FridaState state, FridaReason reason) { + if (state.equals(FridaState.FRIDA_THREAD_STOPPED)) { + requestAttributes(false).thenAccept(__ -> { + for (Object attribute : getCachedAttributes().values()) { + if (attribute instanceof FridaModelTargetRegisterBank) { + FridaModelTargetRegisterBank bank = (FridaModelTargetRegisterBank) attribute; + bank.threadStateChangedSpecific(state, reason); + } + } + }); + } + } + + @Override + public CompletableFuture> readRegistersNamed( + Collection names) { + Map result = new HashMap<>(); + Map els = getCachedElements(); + for (String regname : names) { + if (!elements.containsKey(regname)) { + throw new DebuggerRegisterAccessException("No such register: " + regname); + } + FridaModelTargetRegisterImpl register = + (FridaModelTargetRegisterImpl) els.get(regname); + byte[] bytes = register.getBytes(); + result.put(regname, bytes); + } + ListenerSet ls = getListeners(); + if (ls != null) { + //if (getName().contains("General")) { + ls.fire.registersUpdated(this, result); + //} + } + return CompletableFuture.completedFuture(result); + } + + @Override + public CompletableFuture writeRegistersNamed(Map values) { + Map els = getCachedElements(); + for (Map.Entry ent : values.entrySet()) { + String regname = ent.getKey(); + FridaModelTargetRegisterImpl reg = + (FridaModelTargetRegisterImpl) els.get(regname); + if (reg == null) { + throw new DebuggerRegisterAccessException("No such register: " + regname); + } + BigInteger val = new BigInteger(1, ent.getValue()); + reg.getRegister().setValue(val.toString()); + } + getListeners().fire.registersUpdated(getProxy(), values); + return AsyncUtils.NIL; + } + +} diff --git a/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/impl/FridaModelTargetRegisterImpl.java b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/impl/FridaModelTargetRegisterImpl.java new file mode 100644 index 0000000000..138fb00133 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/impl/FridaModelTargetRegisterImpl.java @@ -0,0 +1,120 @@ +/* ### + * 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.model.impl; + +import java.math.BigInteger; +import java.util.List; +import java.util.Map; + +import agent.frida.manager.FridaValue; +import agent.frida.model.iface2.FridaModelTargetStackFrameRegister; +import ghidra.dbg.target.schema.*; +import ghidra.dbg.util.ConversionUtils; +import ghidra.dbg.util.PathUtils; + +@TargetObjectSchemaInfo( + name = "RegisterValue", + elements = { + @TargetElementType(type = Void.class) }, + attributes = { + @TargetAttributeType(type = Object.class) }) +public class FridaModelTargetRegisterImpl + extends FridaModelTargetObjectImpl + implements FridaModelTargetStackFrameRegister { + + protected static String indexRegister(FridaValue register) { + return register.getKey(); + } + + protected static String keyRegister(FridaValue register) { + return PathUtils.makeKey(indexRegister(register)); + } + + String value = ""; + + public FridaModelTargetRegisterImpl(FridaModelTargetRegisterContainerImpl registers, + FridaValue register) { + super(registers.getModel(), registers, keyRegister(register), register, "Register"); + value = getValue(); + + changeAttributes(List.of(), Map.of( // + CONTAINER_ATTRIBUTE_NAME, registers, // + LENGTH_ATTRIBUTE_NAME, getBitLength(), // + DISPLAY_ATTRIBUTE_NAME, getDescription(0), // + VALUE_ATTRIBUTE_NAME, value == null ? "0" : value, // + MODIFIED_ATTRIBUTE_NAME, false // + ), "Initialized"); + } + + public String getDescription(int level) { + return getValue(); + } + + @Override + public int getBitLength() { + return getRegister().getByteSize() * 8; + } + + @Override + public String getValue() { + String val = getRegister().getValue(); + if (val == null) { + return null; + } + if (!val.startsWith("0x")) { + return val; + } + return val.substring(2); + } + + @Override + public FridaValue getRegister() { + return (FridaValue) getModelObject(); + } + + public byte[] getBytes() { + String oldValue = value; + value = getValue(); + if (value == null) { + return new byte[0]; + } + if (value.startsWith("{")) { + String trim = value.substring(1, value.length() - 1); + String[] split = trim.split(" "); + value = split[0].substring(2) + split[1].substring(2); + } + BigInteger val = new BigInteger(value, 16); + byte[] bytes = ConversionUtils.bigIntegerToBytes(getRegister().getByteSize(), val); + changeAttributes(List.of(), Map.of( // + VALUE_ATTRIBUTE_NAME, value // + ), "Refreshed"); + if (val.longValue() != 0) { + String newval = getDescription(0); + if (newval != null) { + changeAttributes(List.of(), Map.of( // + DISPLAY_ATTRIBUTE_NAME, newval // + ), "Refreshed"); + setModified(!value.equals(oldValue)); + } + } + return bytes; + } + + public String getDisplay() { + return getValue() == null ? getName() : getName() + " : " + getValue(); + } + +} diff --git a/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/impl/FridaModelTargetRootImpl.java b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/impl/FridaModelTargetRootImpl.java new file mode 100644 index 0000000000..9c9693358d --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/impl/FridaModelTargetRootImpl.java @@ -0,0 +1,180 @@ +/* ### + * 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.model.impl; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.concurrent.CompletableFuture; + +import agent.frida.manager.FridaCause; +import agent.frida.manager.FridaReason; +import agent.frida.manager.FridaThread; +import agent.frida.manager.FridaState; +import agent.frida.manager.cmd.FridaAttachCommand; +import agent.frida.model.iface1.FridaModelSelectableObject; +import agent.frida.model.iface2.FridaModelTargetConnector; +import agent.frida.model.iface2.FridaModelTargetRoot; +import agent.frida.model.iface2.FridaModelTargetThread; +import ghidra.dbg.error.DebuggerUserException; +import ghidra.dbg.target.TargetAttachable; +import ghidra.dbg.target.TargetEventScope; +import ghidra.dbg.target.TargetFocusScope; +import ghidra.dbg.target.TargetMethod; +import ghidra.dbg.target.schema.TargetAttributeType; +import ghidra.dbg.target.schema.TargetElementType; +import ghidra.dbg.target.schema.TargetObjectSchema; +import ghidra.dbg.target.schema.TargetObjectSchemaInfo; +import ghidra.dbg.util.PathUtils; + +@TargetObjectSchemaInfo( + name = "Debugger", + elements = { + @TargetElementType(type = Void.class) }, + attributes = { + @TargetAttributeType( + name = "Available", + type = FridaModelTargetAvailableContainerImpl.class, + required = true, + fixed = true), + @TargetAttributeType( + name = "Connectors", + type = FridaModelTargetConnectorContainerImpl.class, + required = true, + fixed = true), + @TargetAttributeType( + name = "Sessions", + type = FridaModelTargetSessionContainerImpl.class, + required = true, + fixed = true), + @TargetAttributeType(type = Void.class) }) +public class FridaModelTargetRootImpl extends FridaModelDefaultTargetModelRoot + implements FridaModelTargetRoot { + + protected final FridaModelTargetAvailableContainerImpl available; + protected final FridaModelTargetConnectorContainerImpl connectors; + protected final FridaModelTargetSessionContainerImpl sessions; + + protected String debugger = "Frida"; // Used by FridaModelTargetEnvironment + + protected FridaModelSelectableObject focus; + + public FridaModelTargetRootImpl(FridaModelImpl impl, TargetObjectSchema schema) { + super(impl, "Debugger", schema); + + this.available = new FridaModelTargetAvailableContainerImpl(this); + this.connectors = new FridaModelTargetConnectorContainerImpl(this); + this.sessions = new FridaModelTargetSessionContainerImpl(this); + + FridaModelTargetConnector defaultConnector = connectors.getDefaultConnector(); + changeAttributes(List.of(), List.of( // + available, // + connectors, // + sessions // + ), Map.of( // + ACCESSIBLE_ATTRIBUTE_NAME, accessible, // + DISPLAY_ATTRIBUTE_NAME, "Debugger", // + FOCUS_ATTRIBUTE_NAME, this, // + SUPPORTED_ATTACH_KINDS_ATTRIBUTE_NAME, FridaModelTargetProcessImpl.SUPPORTED_KINDS, // + TargetMethod.PARAMETERS_ATTRIBUTE_NAME, defaultConnector.getParameters() // + ), "Initialized"); + impl.getManager().addEventsListener(this); + } + + @Override + public FridaModelSelectableObject getFocus() { + return focus; + } + + @Override + public void setDefaultConnector(FridaModelTargetConnector defaultConnector) { + changeAttributes(List.of(), List.of(), + Map.of(TargetMethod.PARAMETERS_ATTRIBUTE_NAME, defaultConnector.getParameters()), + "Default connector changed"); + } + + @Override + public boolean setFocus(FridaModelSelectableObject sel) { + boolean doFire; + synchronized (this) { + doFire = !Objects.equals(this.focus, sel); + if (doFire && focus != null) { + List focusPath = focus.getPath(); + List selPath = sel.getPath(); + doFire = !PathUtils.isAncestor(selPath, focusPath); + } + } + if (doFire) { + this.focus = sel; + changeAttributes(List.of(), List.of(), Map.of( // + TargetFocusScope.FOCUS_ATTRIBUTE_NAME, focus // + ), "Focus changed"); + } + return doFire; + } + + @Override + public CompletableFuture launch(List args) { + FridaModelTargetProcessLaunchConnectorImpl targetConnector = connectors.processLauncher; + return model.gateFuture(targetConnector.launch(args)).exceptionally(exc -> { + throw new DebuggerUserException("Launch failed for " + args); + }); + } + + @Override + public CompletableFuture launch(Map args) { + FridaModelTargetConnector targetConnector = connectors.getDefaultConnector(); + return model.gateFuture(targetConnector.launch(args)).exceptionally(exc -> { + throw new DebuggerUserException("Launch failed for " + args); + }); + } + + @Override + public CompletableFuture attach(TargetAttachable attachable) { + FridaModelTargetProcessAttachByPidConnectorImpl targetConnector = + connectors.processAttacherByPid; + String key = attachable.getName(); + Map map = new HashMap<>(); + map.put("Pid", key.substring(1, key.length() - 1)); + return model.gateFuture(targetConnector.launch(map)).exceptionally(exc -> { + throw new DebuggerUserException("Launch failed for " + key); + }); + } + + @Override + public CompletableFuture attach(long pid) { + return model.gateFuture( + getManager().execute(new FridaAttachCommand(getManager(), Long.toString(pid))) + .thenApply(__ -> null)); + } + + @Override + public void threadStateChanged(FridaThread thread, FridaState state, FridaCause cause, + FridaReason reason) { + FridaModelTargetThread targetThread = + (FridaModelTargetThread) getModel().getModelObject(thread); + changeAttributes(List.of(), List.of(), Map.of( // + TargetEventScope.EVENT_OBJECT_ATTRIBUTE_NAME, targetThread // + ), reason.desc()); + } + + @Override + public boolean isAccessible() { + return true; + } + +} diff --git a/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/impl/FridaModelTargetSessionAttributesEnvironmentImpl.java b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/impl/FridaModelTargetSessionAttributesEnvironmentImpl.java new file mode 100644 index 0000000000..3cf7ec3dcd --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/impl/FridaModelTargetSessionAttributesEnvironmentImpl.java @@ -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.model.impl; + +import java.util.List; +import java.util.Map; + +import agent.frida.manager.FridaSession; +import agent.frida.model.iface2.FridaModelTargetSessionAttributes; +import agent.frida.model.iface2.FridaModelTargetSessionAttributesEnvironment; +import ghidra.dbg.target.schema.TargetAttributeType; +import ghidra.dbg.target.schema.TargetObjectSchemaInfo; + +@TargetObjectSchemaInfo( + name = "SessionAttributesEnvironment", + attributes = { + @TargetAttributeType(type = Object.class) + }) +public class FridaModelTargetSessionAttributesEnvironmentImpl extends FridaModelTargetObjectImpl + implements FridaModelTargetSessionAttributesEnvironment { + + FridaSession session; + + public FridaModelTargetSessionAttributesEnvironmentImpl( + FridaModelTargetSessionAttributes attributes) { + super(attributes.getModel(), attributes, "Environment", "SessionAttributesEnvironment"); + + session = (FridaSession) getModelObject(); + + changeAttributes(List.of(), List.of(), Map.of( // + "Debugger", "Frida", // + "Debugger Attached", session.getAttribute("debugger"), // + "Debugger Version", session.getAttribute("version"), // + "Code Signing", session.getAttribute("codeSigning"), // + "Page Size", session.getAttribute("pageSize"), // + "Pointer Size", session.getAttribute("pointerSize"), // + "Heap Size", session.getAttribute("heapSize"), // + "Runtime", session.getAttribute("runtime"), // + "Kernel", session.getAttribute("kernel") // + ), "Initialized"); + + getManager().addEventsListener(this); + } + +} diff --git a/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/impl/FridaModelTargetSessionAttributesImpl.java b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/impl/FridaModelTargetSessionAttributesImpl.java new file mode 100644 index 0000000000..422202b38f --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/impl/FridaModelTargetSessionAttributesImpl.java @@ -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.model.impl; + +import java.util.List; +import java.util.Map; + +import agent.frida.manager.FridaSession; +import agent.frida.model.iface2.FridaModelTargetSession; +import agent.frida.model.iface2.FridaModelTargetSessionAttributes; +import ghidra.dbg.target.schema.*; + +@TargetObjectSchemaInfo( + name = "SessionAttributes", + elements = { + @TargetElementType(type = Void.class) + }, + attributes = { + @TargetAttributeType( + name = "Environment", + type = FridaModelTargetSessionAttributesEnvironmentImpl.class, + fixed = true), + @TargetAttributeType( + name = "Platform", + type = FridaModelTargetSessionAttributesPlatformImpl.class, + fixed = true), + @TargetAttributeType(type = Void.class) + }) +public class FridaModelTargetSessionAttributesImpl extends FridaModelTargetObjectImpl + implements FridaModelTargetSessionAttributes { + + protected final FridaModelTargetSessionAttributesPlatformImpl platformAttributes; + protected final FridaModelTargetSessionAttributesEnvironmentImpl environment; + + public FridaModelTargetSessionAttributesImpl(FridaModelTargetSession session) { + super(session.getModel(), session, "Attributes", "SessionAttributes"); + + this.platformAttributes = new FridaModelTargetSessionAttributesPlatformImpl(this); + this.environment = new FridaModelTargetSessionAttributesEnvironmentImpl(this); + + requestAttributes(false); + + FridaSession s = (FridaSession) session.getModelObject(); + changeAttributes(List.of(), List.of( // + platformAttributes, // + environment // + ), Map.of( // + ARCH_ATTRIBUTE_NAME, s.getAttribute("arch"), // + DEBUGGER_ATTRIBUTE_NAME, "Frida", // + OS_ATTRIBUTE_NAME, s.getAttribute("os") // + //ENDIAN_ATTRIBUTE_NAME, orderStr // + ), "Initialized"); + + getManager().addEventsListener(this); + } + +} diff --git a/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/impl/FridaModelTargetSessionAttributesPlatformImpl.java b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/impl/FridaModelTargetSessionAttributesPlatformImpl.java new file mode 100644 index 0000000000..d64f732858 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/impl/FridaModelTargetSessionAttributesPlatformImpl.java @@ -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.model.impl; + +import java.util.List; +import java.util.Map; + +import agent.frida.manager.*; +import agent.frida.model.iface2.FridaModelTargetSessionAttributes; +import agent.frida.model.iface2.FridaModelTargetSessionAttributesPlatform; +import ghidra.dbg.target.schema.*; + +@TargetObjectSchemaInfo( + name = "SessionAttributesPlatform", + elements = { + @TargetElementType(type = Void.class) + }, + attributes = { + @TargetAttributeType(type = Object.class) + }) +public class FridaModelTargetSessionAttributesPlatformImpl extends FridaModelTargetObjectImpl + implements FridaModelTargetSessionAttributesPlatform { + + static String ARCH_ATTRIBUTE_NAME = "Arch"; + static String OS_ATTRIBUTE_NAME = "OS"; + static String DEBUGGER_ATTRIBUTE_NAME = "Debugger"; + + FridaSession session; + + public FridaModelTargetSessionAttributesPlatformImpl( + FridaModelTargetSessionAttributes attributes) { + super(attributes.getModel(), attributes, "Platform", "SessionAttributesPlatform"); + + session = (FridaSession) getModelObject(); + + changeAttributes(List.of(), List.of(), Map.of( // + ARCH_ATTRIBUTE_NAME, session.getAttribute("arch"), // + OS_ATTRIBUTE_NAME, session.getAttribute("os"), // + DEBUGGER_ATTRIBUTE_NAME, "Frida" // + ), "Initialized"); + + getManager().addEventsListener(this); + } + +} diff --git a/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/impl/FridaModelTargetSessionContainerImpl.java b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/impl/FridaModelTargetSessionContainerImpl.java new file mode 100644 index 0000000000..80a6da159f --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/impl/FridaModelTargetSessionContainerImpl.java @@ -0,0 +1,93 @@ +/* ### + * 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.model.impl; + +import java.util.List; +import java.util.Map; +import java.util.concurrent.CompletableFuture; +import java.util.stream.Collectors; + +import agent.frida.manager.FridaCause; +import agent.frida.manager.FridaSession; +import agent.frida.model.iface2.FridaModelTargetRoot; +import agent.frida.model.iface2.FridaModelTargetSession; +import agent.frida.model.iface2.FridaModelTargetSessionContainer; +import ghidra.dbg.target.TargetObject; +import ghidra.dbg.target.schema.TargetAttributeType; +import ghidra.dbg.target.schema.TargetElementType; +import ghidra.dbg.target.schema.TargetObjectSchemaInfo; + +@TargetObjectSchemaInfo( + name = "SessionContainer", + elements = { + @TargetElementType(type = FridaModelTargetSessionImpl.class) }, + attributes = { + @TargetAttributeType(type = Void.class) }, + canonicalContainer = true) +public class FridaModelTargetSessionContainerImpl extends FridaModelTargetObjectImpl + implements FridaModelTargetSessionContainer { + + public FridaModelTargetSessionContainerImpl(FridaModelTargetRoot root) { + super(root.getModel(), root, "Sessions", "SessionContainer"); + + getManager().addEventsListener(this); + } + + @Override + public void sessionAdded(FridaSession sess, FridaCause cause) { + FridaModelTargetSession session = getTargetSession(sess); + changeElements(List.of(), List.of(session), Map.of(), "Added"); + } + + @Override + public void sessionReplaced(FridaSession sess, FridaCause cause) { + FridaModelTargetSession session = getTargetSession(sess); + changeElements(List.of(), List.of(session), Map.of(), "Replaced"); + } + + @Override + public void sessionRemoved(String sessionId, FridaCause cause) { + changeElements(List.of( // + sessionId // + ), List.of(), Map.of(), "Removed"); + } + + @Override + public synchronized FridaModelTargetSession getTargetSession(FridaSession session) { + TargetObject targetObject = getMapObject(session); + if (targetObject != null) { + FridaModelTargetSession targetSession = (FridaModelTargetSession) targetObject; + targetSession.setModelObject(session); + return targetSession; + } + return new FridaModelTargetSessionImpl(this, session); + } + + @Override + public CompletableFuture requestElements(boolean refresh) { + return getManager().listSessions().thenAccept(byIID -> { + List sessions; + synchronized (this) { + sessions = byIID.values() + .stream() + .map(this::getTargetSession) + .collect(Collectors.toList()); + } + setElements(sessions, "Refreshed"); + }); + } + +} diff --git a/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/impl/FridaModelTargetSessionImpl.java b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/impl/FridaModelTargetSessionImpl.java new file mode 100644 index 0000000000..11e61a69f8 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/impl/FridaModelTargetSessionImpl.java @@ -0,0 +1,147 @@ +/* ### + * 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.model.impl; + +import java.util.List; +import java.util.Map; +import java.util.concurrent.CompletableFuture; + +import agent.frida.frida.FridaClient; +import agent.frida.manager.FridaSession; +import agent.frida.manager.cmd.FridaContinueCommand; +import agent.frida.model.iface1.FridaModelTargetInterpreter; +import agent.frida.model.iface2.FridaModelTargetModuleContainer; +import agent.frida.model.iface2.FridaModelTargetProcessContainer; +import agent.frida.model.iface2.FridaModelTargetSession; +import agent.frida.model.methods.FridaModelTargetUnloadScriptImpl; +import ghidra.dbg.target.schema.TargetAttributeType; +import ghidra.dbg.target.schema.TargetElementType; +import ghidra.dbg.target.schema.TargetObjectSchemaInfo; +import ghidra.dbg.util.PathUtils; + +@TargetObjectSchemaInfo( + name = "Session", + elements = { + @TargetElementType(type = Void.class) }, + attributes = { + @TargetAttributeType( + name = "Attributes", + type = FridaModelTargetSessionAttributesImpl.class, + fixed = true), + @TargetAttributeType( + name = "Processes", + type = FridaModelTargetProcessContainerImpl.class, + required = true, + fixed = true), + @TargetAttributeType( + name = "Modules", + type = FridaModelTargetModuleContainerImpl.class, + required = true, + fixed = true), + @TargetAttributeType(type = Object.class) }) +public class FridaModelTargetSessionImpl extends FridaModelTargetObjectImpl + implements FridaModelTargetSession { + + protected static final String FRIDA_PROMPT = "(frida)"; + private Integer base = 10; + + // NB: This should almost certainly always be implemented by the root of the object tree + + protected static String indexSession(FridaSession session) { + return FridaClient.getId(session); + } + + protected static String keySession(FridaSession session) { + return PathUtils.makeKey(indexSession(session)); + } + + protected final FridaModelTargetModuleContainer modules; + protected final FridaModelTargetSessionAttributesImpl attrs; + protected final FridaModelTargetProcessContainerImpl processes; + + private FridaModelTargetUnloadScriptImpl unload; + private FridaModelTargetKernelImpl kernel; + protected String debugger = "frida"; // Used by FridaModelTargetEnvironment + + public FridaModelTargetSessionImpl(FridaModelTargetSessionContainerImpl sessions, + FridaSession session) { + super(sessions.getModel(), sessions, keySession(session), session, "Session"); + + this.attrs = new FridaModelTargetSessionAttributesImpl(this); + this.processes = new FridaModelTargetProcessContainerImpl(this); + this.modules = new FridaModelTargetModuleContainerImpl(this); + + this.unload = new FridaModelTargetUnloadScriptImpl(this, ""); + + changeAttributes(List.of(), List.of( // + attrs, // + processes, // + modules // + ), Map.of( // + //DISPLAY_ATTRIBUTE_NAME, getDescription(0), // + PROMPT_ATTRIBUTE_NAME, FridaModelTargetInterpreter.FRIDA_PROMPT, // + STATE_ATTRIBUTE_NAME, TargetExecutionState.ALIVE, // + unload.getName(), unload // + ), "Initialized"); + + if (session.getAttribute("kernel").equals("true")) { + this.kernel = new FridaModelTargetKernelImpl(this); + changeAttributes(List.of(), List.of( // + kernel // + ), Map.of(), "Initialized"); + } + + getManager().addEventsListener(this); + } + + @Override + public String getDisplay() { + FridaSession session = (FridaSession) getModelObject(); + String pidstr = FridaClient.getId(session.getProcess()); + if (base == 16) { + pidstr = "0x" + pidstr; + } + else { + pidstr = Long.toString(Long.parseLong(pidstr, 16)); + } + return "[" + pidstr + "]"; + } + + @Override + public CompletableFuture setActive() { + return CompletableFuture.completedFuture(null); + } + + @Override + public FridaModelTargetProcessContainer getProcesses() { + return processes; + } + + @Override + public FridaModelTargetModuleContainer getModules() { + return modules; + } + + public FridaSession getSession() { + return (FridaSession) getModelObject(); + } + + @Override + public CompletableFuture resume() { + return model.gateFuture(getManager().execute(new FridaContinueCommand(getManager()))); + } + +} diff --git a/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/impl/FridaModelTargetStackFrameImpl.java b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/impl/FridaModelTargetStackFrameImpl.java new file mode 100644 index 0000000000..f0d6772613 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/impl/FridaModelTargetStackFrameImpl.java @@ -0,0 +1,182 @@ +/* ### + * 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.model.impl; + +import java.util.List; +import java.util.Map; + +import agent.frida.frida.FridaClient; +import agent.frida.manager.*; +import agent.frida.model.iface1.FridaModelTargetFocusScope; +import agent.frida.model.iface2.*; +import ghidra.dbg.target.TargetFocusScope; +import ghidra.dbg.target.TargetObject; +import ghidra.dbg.target.schema.*; +import ghidra.dbg.target.schema.TargetObjectSchema.ResyncMode; +import ghidra.dbg.util.PathUtils; +import ghidra.program.model.address.Address; + +@TargetObjectSchemaInfo( + name = "StackFrame", + attributeResync = ResyncMode.ALWAYS, + elements = { + @TargetElementType(type = Void.class) }, + attributes = { + @TargetAttributeType( + name = "Function", + type = FridaModelTargetFunctionImpl.class), + @TargetAttributeType( + name = FridaModelTargetStackFrame.FUNC_ATTRIBUTE_NAME, + type = String.class), + @TargetAttributeType( + name = FridaModelTargetStackFrame.INST_OFFSET_ATTRIBUTE_NAME, + type = String.class), + @TargetAttributeType( + name = FridaModelTargetStackFrame.FRAME_OFFSET_ATTRIBUTE_NAME, + type = String.class), + @TargetAttributeType( + name = FridaModelTargetStackFrame.RETURN_OFFSET_ATTRIBUTE_NAME, + type = String.class), + @TargetAttributeType( + name = FridaModelTargetStackFrame.CALL_FRAME_OFFSET_ATTRIBUTE_NAME, + type = String.class), + @TargetAttributeType( + name = FridaModelTargetStackFrame.STACK_OFFSET_ATTRIBUTE_NAME, + type = String.class), + @TargetAttributeType(type = Void.class) }) +public class FridaModelTargetStackFrameImpl extends FridaModelTargetObjectImpl + implements FridaModelTargetStackFrame { + + protected static String indexFrame(FridaFrame frame) { + return FridaClient.getId(frame); + } + + protected static String keyFrame(FridaFrame frame) { + return PathUtils.makeKey(indexFrame(frame)); + } + + protected final FridaModelTargetThread thread; + + protected Address pc; + protected String func; + protected String file; + protected String module; + protected Long line; + protected String display; + + private final FridaModelTargetFunctionImpl function; + + //private Long frameOffset = -1L; + //private Long stackOffset = -1L; + //private Long callFrameOffset = -1L; + //private Long returnOffset = -1L; + + public FridaModelTargetStackFrameImpl(FridaModelTargetStack stack, FridaModelTargetThread thread, + FridaFrame frame) { + super(stack.getModel(), stack, keyFrame(frame), frame, "StackFrame"); + this.thread = thread; + this.pc = getModel().getAddressSpace("ram").getAddress(-1); + + this.function = new FridaModelTargetFunctionImpl(this, frame.getFunction()); + + changeAttributes(List.of(), List.of( + function // + ), Map.of( // + DISPLAY_ATTRIBUTE_NAME, display = computeDisplay(frame), // + PC_ATTRIBUTE_NAME, pc // + ), "Initialized"); + setFrame(frame); + + getManager().addEventsListener(this); + } + + protected static String computeDisplay(FridaFrame frame) { + if (frame.getFunctionName() == null) { + frame.getPC(); + return String.format("#%s 0x%s", FridaClient.getId(frame), Long.toHexString(frame.getPC())); + } + return String.format("#%s 0x%s in %s ()", FridaClient.getId(frame), Long.toHexString(frame.getPC()), + frame.getFunctionName()); + } + + @Override + public void threadSelected(FridaThread eventThread, FridaFrame eventFrame, FridaCause cause) { + if (eventFrame != null && eventFrame.equals(getFrame())) { + ((FridaModelTargetFocusScope) searchForSuitable(TargetFocusScope.class)).setFocus(this); + } + } + + @Override + public void setFrame(FridaFrame frame) { + setModelObject(frame); + Long address = frame.getPC(); + long lval = address == null ? -1 : address; + this.pc = getModel().getAddressSpace("ram").getAddress(lval); + this.func = frame.getFunctionName(); + if (func == null) { + func = "UNKNOWN"; + } + this.file = frame.getFileName(); + if (file == null) { + file = "UNKNOWN"; + } + this.module = frame.getModuleName(); + if (module == null) { + module = "UNKNOWN"; + } + this.line = frame.getLineNumber(); + //this.frameOffset = frame.GetFP().longValue(); + //this.stackOffset = frame.GetSP().longValue(); + //this.callFrameOffset = frame.GetCFA().longValue(); + + changeAttributes(List.of(), List.of(), Map.of( // + PC_ATTRIBUTE_NAME, pc, // + //DISPLAY_ATTRIBUTE_NAME, display = getDescription(0), //computeDisplay(frame), // + FUNC_ATTRIBUTE_NAME, func // + //FILE_ATTRIBUTE_NAME, file, // + //MODULE_ATTRIBUTE_NAME, module, // + //LINE_ATTRIBUTE_NAME, line // + //INST_OFFSET_ATTRIBUTE_NAME, Long.toHexString(lval), // + //FRAME_OFFSET_ATTRIBUTE_NAME, Long.toHexString(frameOffset), // + //STACK_OFFSET_ATTRIBUTE_NAME, Long.toHexString(stackOffset), // + //CALL_FRAME_OFFSET_ATTRIBUTE_NAME, Long.toHexString(callFrameOffset) // + ), "Refreshed"); + } + + @Override + public TargetObject getThread() { + return thread; + } + + public FridaFrame getFrame() { + return (FridaFrame) getModelObject(); + } + + @Override + public Address getPC() { + return pc; + } + + @Override + public FridaModelTargetProcess getProcess() { + return ((FridaModelTargetThreadImpl) thread).getProcess(); + } + + public void threadStateChangedSpecific(FridaState state, FridaReason reason) { + setFrame(getFrame()); + } + +} diff --git a/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/impl/FridaModelTargetStackImpl.java b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/impl/FridaModelTargetStackImpl.java new file mode 100644 index 0000000000..99d36fa42f --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/impl/FridaModelTargetStackImpl.java @@ -0,0 +1,100 @@ +/* ### + * 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.model.impl; + +import java.util.List; +import java.util.Map; +import java.util.concurrent.CompletableFuture; +import java.util.stream.Collectors; + +import agent.frida.manager.FridaFrame; +import agent.frida.manager.FridaReason; +import agent.frida.manager.FridaState; +import agent.frida.model.iface2.FridaModelTargetProcess; +import agent.frida.model.iface2.FridaModelTargetStack; +import agent.frida.model.iface2.FridaModelTargetStackFrame; +import agent.frida.model.iface2.FridaModelTargetThread; +import ghidra.dbg.target.TargetObject; +import ghidra.dbg.target.schema.TargetAttributeType; +import ghidra.dbg.target.schema.TargetElementType; +import ghidra.dbg.target.schema.TargetObjectSchema.ResyncMode; +import ghidra.dbg.target.schema.TargetObjectSchemaInfo; +import ghidra.util.datastruct.WeakValueHashMap; + +@TargetObjectSchemaInfo( + name = "Stack", + elementResync = ResyncMode.ALWAYS, + elements = { + @TargetElementType(type = FridaModelTargetStackFrameImpl.class) + }, + attributes = { + @TargetAttributeType(type = Void.class) + }, + canonicalContainer = true) +public class FridaModelTargetStackImpl extends FridaModelTargetObjectImpl + implements FridaModelTargetStack { + + protected final FridaModelTargetThread thread; + + public static final String NAME = "Stack"; + + protected final Map framesByLevel = + new WeakValueHashMap<>(); + + public FridaModelTargetStackImpl(FridaModelTargetThread thread, FridaModelTargetProcess process) { + super(thread.getModel(), thread, NAME, "Stack"); + this.thread = thread; + requestElements(false); + } + + @Override + public CompletableFuture requestElements(boolean refresh) { + return getManager().listStackFrames(thread.getThread()).thenAccept(f -> { + List frames; + synchronized (this) { + frames = + f.values().stream().map(this::getTargetFrame).collect(Collectors.toList()); + } + setElements(frames, Map.of(), "Refreshed"); + }); + } + + @Override + public synchronized FridaModelTargetStackFrame getTargetFrame(FridaFrame frame) { + return framesByLevel.compute(frame.getFrameID(), (l, f) -> { + if (f == null) { + return new FridaModelTargetStackFrameImpl(this, thread, frame); + } + f.setFrame(frame); + return f; + }); + } + + public void threadStateChangedSpecific(FridaState state, FridaReason reason) { + if (state.equals(FridaState.FRIDA_THREAD_STOPPED)) { + requestElements(true).thenAccept(__ -> { + for (TargetObject element : getCachedElements().values()) { + if (element instanceof FridaModelTargetStackFrame) { + FridaModelTargetStackFrameImpl frame = + (FridaModelTargetStackFrameImpl) element; + frame.threadStateChangedSpecific(state, reason); + } + } + }); + } + } + +} diff --git a/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/impl/FridaModelTargetSymbolContainerImpl.java b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/impl/FridaModelTargetSymbolContainerImpl.java new file mode 100644 index 0000000000..373abcb58f --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/impl/FridaModelTargetSymbolContainerImpl.java @@ -0,0 +1,87 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.frida.model.impl; + +import java.util.List; +import java.util.Map; +import java.util.concurrent.CompletableFuture; +import java.util.stream.Collectors; + +import agent.frida.manager.FridaSymbol; +import agent.frida.model.iface2.FridaModelTargetSymbolContainer; +import agent.frida.model.methods.*; +import ghidra.dbg.target.TargetObject; +import ghidra.dbg.target.schema.TargetAttributeType; +import ghidra.dbg.target.schema.TargetElementType; +import ghidra.dbg.target.schema.TargetObjectSchema.ResyncMode; +import ghidra.dbg.target.schema.TargetObjectSchemaInfo; + +@TargetObjectSchemaInfo( + name = "SymbolContainer", + elements = { + @TargetElementType(type = FridaModelTargetSymbolImpl.class) }, + elementResync = ResyncMode.ONCE, + attributes = { + @TargetAttributeType(type = Object.class) }, + canonicalContainer = true) +public class FridaModelTargetSymbolContainerImpl extends FridaModelTargetObjectImpl + implements FridaModelTargetSymbolContainer { + + protected final FridaModelTargetModuleImpl module; + private FridaModelTargetSymbolFromAddressImpl fromAddr; + private FridaModelTargetSymbolFromNameImpl fromName; + private FridaModelTargetSymbolLoadImpl load; + + public FridaModelTargetSymbolContainerImpl(FridaModelTargetModuleImpl module) { + super(module.getModel(), module, "Symbols", "SymbolContainer"); + this.module = module; + + this.fromAddr = new FridaModelTargetSymbolFromAddressImpl(this); + this.fromName = new FridaModelTargetSymbolFromNameImpl(this); + this.load = new FridaModelTargetSymbolLoadImpl(this); + this.changeAttributes(List.of(), List.of( // + fromAddr, // + fromName, // + load // + ), Map.of(), "Initialized"); + + } + + @Override + public CompletableFuture requestElements(boolean refresh) { + return getManager().listModuleSymbols(module.getModule()).thenAccept(byName -> { + List symbols; + synchronized (this) { + symbols = byName.values() + .stream() + .map(this::getTargetSymbol) + .collect(Collectors.toList()); + } + setElements(symbols, Map.of(), "Refreshed"); + }); + } + + @Override + public synchronized FridaModelTargetSymbolImpl getTargetSymbol(FridaSymbol symbol) { + TargetObject targetObject = getMapObject(symbol); + if (targetObject != null) { + FridaModelTargetSymbolImpl targetSymbol = (FridaModelTargetSymbolImpl) targetObject; + targetSymbol.setModelObject(symbol); + return targetSymbol; + } + return new FridaModelTargetSymbolImpl(this, symbol); + } +} diff --git a/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/impl/FridaModelTargetSymbolImpl.java b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/impl/FridaModelTargetSymbolImpl.java new file mode 100644 index 0000000000..feef23721b --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/impl/FridaModelTargetSymbolImpl.java @@ -0,0 +1,122 @@ +/* ### + * 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.model.impl; + +import java.util.List; +import java.util.Map; + +import agent.frida.manager.FridaSymbol; +import agent.frida.model.iface2.FridaModelTargetSymbol; +import agent.frida.model.methods.FridaModelTargetFunctionInterceptorImpl; +import agent.frida.model.methods.FridaModelTargetUnloadScriptImpl; +import ghidra.dbg.target.TargetObject; +import ghidra.dbg.target.TargetSymbol; +import ghidra.dbg.target.schema.TargetAttributeType; +import ghidra.dbg.target.schema.TargetElementType; +import ghidra.dbg.target.schema.TargetObjectSchemaInfo; +import ghidra.dbg.util.PathUtils; +import ghidra.program.model.address.Address; +import ghidra.program.model.address.AddressFormatException; + +@TargetObjectSchemaInfo( + name = "Symbol", + elements = { + @TargetElementType(type = Void.class) }, + attributes = { + @TargetAttributeType( + name = TargetSymbol.NAMESPACE_ATTRIBUTE_NAME, + type = FridaModelTargetSymbolContainerImpl.class), + @TargetAttributeType(name = TargetObject.VALUE_ATTRIBUTE_NAME, type = Address.class), + @TargetAttributeType(name = TargetSymbol.SIZE_ATTRIBUTE_NAME, type = long.class), + @TargetAttributeType(name = "Name", type = String.class), + @TargetAttributeType(name = "Size", type = long.class), + @TargetAttributeType(name = "TypeId", type = int.class), + @TargetAttributeType(name = "Tag", type = int.class), + @TargetAttributeType(type = Object.class) }) +public class FridaModelTargetSymbolImpl extends FridaModelTargetObjectImpl + implements FridaModelTargetSymbol { + protected static String indexSymbol(FridaSymbol symbol) { + return symbol.getName(); + } + + protected static String keySymbol(FridaSymbol symbol) { + return PathUtils.makeKey(indexSymbol(symbol)); + } + + protected final boolean constant; + protected Address value; + protected long size; + private FridaModelTargetFunctionInterceptorImpl intercept; + private FridaModelTargetUnloadScriptImpl unload; + + public FridaModelTargetSymbolImpl(FridaModelTargetSymbolContainerImpl symbols, + FridaSymbol symbol) { + super(symbols.getModel(), symbols, keySymbol(symbol), symbol, "Symbol"); + this.constant = false; + try { + this.value = symbols.getModel() + .getAddressSpace("ram") + .getAddress(symbol.getAddress()); + } catch (AddressFormatException e) { + e.printStackTrace(); + } + this.size = symbol.getSize(); + this.intercept = new FridaModelTargetFunctionInterceptorImpl(this); + this.unload = new FridaModelTargetUnloadScriptImpl(this, intercept.getName()); + + changeAttributes(List.of(), List.of(), Map.of( // + DISPLAY_ATTRIBUTE_NAME, getDescription(0), // + NAMESPACE_ATTRIBUTE_NAME, symbols, // + VALUE_ATTRIBUTE_NAME, value, // + SIZE_ATTRIBUTE_NAME, size // + ), "Initialized"); + changeAttributes(List.of(), List.of(), Map.of( // + "Name", symbol.getName(), // + "Address", symbol.getAddress(), // + "Size", size, // + //"Module", symbol.getModule(), // + "Type", symbol.getType(), // + "IsGlobal", symbol.isGlobal(), // + intercept.getName(), intercept, // + unload.getName(), unload // + ), "Initialized"); + if (symbol.getSectionId() != null) { + changeAttributes(List.of(), List.of(), Map.of( // + "Section", symbol.getSectionId()// + ), "Initialized"); + } + } + + public String getDescription(int level) { + FridaSymbol symbol = (FridaSymbol) getModelObject(); + return symbol.getName(); + } + + @Override + public boolean isConstant() { + return constant; + } + + @Override + public Address getValue() { + return value; + } + + @Override + public long getSize() { + return size; + } +} diff --git a/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/impl/FridaModelTargetThreadContainerImpl.java b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/impl/FridaModelTargetThreadContainerImpl.java new file mode 100644 index 0000000000..50dcb0ff2e --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/impl/FridaModelTargetThreadContainerImpl.java @@ -0,0 +1,184 @@ +/* ### + * 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.model.impl; + +import java.util.List; +import java.util.Map; +import java.util.concurrent.CompletableFuture; + +import agent.frida.frida.FridaClient; +import agent.frida.manager.FridaCause; +import agent.frida.manager.FridaProcess; +import agent.frida.manager.FridaReason; +import agent.frida.manager.FridaState; +import agent.frida.manager.FridaThread; +import agent.frida.model.iface1.FridaModelTargetConfigurable; +import agent.frida.model.iface2.FridaModelTargetProcess; +import agent.frida.model.iface2.FridaModelTargetThread; +import agent.frida.model.iface2.FridaModelTargetThreadContainer; +import agent.frida.model.methods.FridaModelTargetThreadSleepImpl; +import agent.frida.model.methods.FridaModelTargetThreadStalkImpl; +import agent.frida.model.methods.FridaModelTargetUnloadScriptImpl; +import ghidra.async.AsyncUtils; +import ghidra.dbg.error.DebuggerIllegalArgumentException; +import ghidra.dbg.target.TargetConfigurable; +import ghidra.dbg.target.TargetObject; +import ghidra.dbg.target.schema.TargetAttributeType; +import ghidra.dbg.target.schema.TargetElementType; +import ghidra.dbg.target.schema.TargetObjectSchema.ResyncMode; +import ghidra.dbg.target.schema.TargetObjectSchemaInfo; + +@TargetObjectSchemaInfo( + name = "ThreadContainer", + elementResync = ResyncMode.ALWAYS, + elements = { // + @TargetElementType(type = FridaModelTargetThreadImpl.class) // + }, + attributes = { // + @TargetAttributeType(name = TargetConfigurable.BASE_ATTRIBUTE_NAME, type = Integer.class), // + @TargetAttributeType(type = Object.class) // + }, + canonicalContainer = true) +public class FridaModelTargetThreadContainerImpl extends FridaModelTargetObjectImpl + implements FridaModelTargetThreadContainer, FridaModelTargetConfigurable { + + protected final FridaProcess process; + private FridaModelTargetThreadSleepImpl sleep; + private FridaModelTargetThreadStalkImpl stalk; + private FridaModelTargetUnloadScriptImpl unload; + + public FridaModelTargetThreadContainerImpl(FridaModelTargetProcessImpl process) { + super(process.getModel(), process, "Threads", "ThreadContainer"); + this.process = process.getProcess(); + this.changeAttributes(List.of(), Map.of(BASE_ATTRIBUTE_NAME, 16), "Initialized"); + + this.sleep = new FridaModelTargetThreadSleepImpl(this); + this.stalk = new FridaModelTargetThreadStalkImpl(this); + this.unload = new FridaModelTargetUnloadScriptImpl(this, stalk.getName()); + this.changeAttributes(List.of(), List.of( // + sleep, // + stalk, // + unload // + ), Map.of(), "Initialized"); + + getManager().addEventsListener(this); + requestElements(true); + } + + @Override + public void threadCreated(FridaThread thread, FridaCause cause) { + changeElements(List.of(), List.of(getTargetThread(thread)), Map.of(), "Created"); + FridaModelTargetThread targetThread = getTargetThread(thread); + changeElements(List.of(), List.of(targetThread), Map.of(), "Created"); + targetThread.threadStateChangedSpecific(FridaState.FRIDA_THREAD_UNINTERRUPTIBLE, + FridaReason.getReason(null)); + getListeners().fire.event(getProxy(), targetThread, TargetEventType.THREAD_CREATED, + "Thread " + FridaClient.getId(thread) + " started", List.of(targetThread)); + } + + @Override + public void threadReplaced(FridaThread thread, FridaCause cause) { + changeElements(List.of(), List.of(getTargetThread(thread)), Map.of(), "Created"); + FridaModelTargetThread targetThread = getTargetThread(thread); + changeElements(List.of(), List.of(targetThread), Map.of(), "Created"); + } + + @Override + public void threadExited(FridaThread thread, FridaCause cause) { + if (thread == null) { + return; + } + String threadId = FridaModelTargetThreadImpl.indexThread(thread); + FridaModelTargetThread targetThread = (FridaModelTargetThread) getMapObject(thread); + if (targetThread != null) { + getListeners().fire.event(getProxy(), targetThread, TargetEventType.THREAD_EXITED, + "Thread " + threadId + " exited", List.of(targetThread)); + } + changeElements(List.of( // + threadId // + ), List.of(), Map.of(), "Exited"); + } + + @Override + public void threadStateChanged(FridaThread thread, FridaState state, FridaCause cause, + FridaReason reason) { + FridaModelTargetThread targetThread = getTargetThread(thread); + TargetEventType eventType = getEventType(state, cause, reason); + getListeners().fire.event(getProxy(), targetThread, eventType, + "Thread " + FridaClient.getId(thread) + " state changed", List.of(targetThread)); + targetThread.threadStateChangedSpecific(state, reason); + } + + private TargetEventType getEventType(FridaState state, FridaCause cause, FridaReason reason) { + switch (state) { + case FRIDA_THREAD_WAITING: + return TargetEventType.RUNNING; + case FRIDA_THREAD_HALTED: + return TargetEventType.PROCESS_EXITED; + case FRIDA_THREAD_UNINTERRUPTIBLE: + return TargetEventType.PROCESS_CREATED; + case FRIDA_THREAD_STOPPED: + return TargetEventType.STOPPED; + case FRIDA_THREAD_RUNNING: + return TargetEventType.RUNNING; + default: + return TargetEventType.STOPPED; + } + } + + @Override + public CompletableFuture requestElements(boolean refresh) { + if (refresh) { + listeners.fire.invalidateCacheRequested(this); + } + return getManager().listThreads(process); + } + + @Override + public synchronized FridaModelTargetThread getTargetThread(FridaThread thread) { + TargetObject targetObject = getMapObject(thread); + if (targetObject != null) { + FridaModelTargetThread targetThread = (FridaModelTargetThread) targetObject; + targetThread.setModelObject(thread); + return targetThread; + } + return new FridaModelTargetThreadImpl(this, (FridaModelTargetProcess) parent, thread); + } + + @Override + public CompletableFuture writeConfigurationOption(String key, Object value) { + switch (key) { + case BASE_ATTRIBUTE_NAME: + if (value instanceof Integer) { + this.changeAttributes(List.of(), Map.of(BASE_ATTRIBUTE_NAME, value), + "Modified"); + for (TargetObject child : getCachedElements().values()) { + if (child instanceof FridaModelTargetThreadImpl) { + FridaModelTargetThreadImpl targetThread = + (FridaModelTargetThreadImpl) child; + targetThread.setBase(value); + } + } + } + else { + throw new DebuggerIllegalArgumentException("Base should be numeric"); + } + default: + } + return AsyncUtils.NIL; + } + +} diff --git a/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/impl/FridaModelTargetThreadImpl.java b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/impl/FridaModelTargetThreadImpl.java new file mode 100644 index 0000000000..b13634c245 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/impl/FridaModelTargetThreadImpl.java @@ -0,0 +1,196 @@ +/* ### + * 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.model.impl; + +import java.util.List; +import java.util.Map; +import java.util.concurrent.CompletableFuture; + +import agent.frida.frida.FridaClient; +import agent.frida.manager.*; +import agent.frida.manager.FridaReason.Reasons; +import agent.frida.model.iface1.FridaModelTargetFocusScope; +import agent.frida.model.iface2.*; +import agent.frida.model.methods.FridaModelTargetThreadStalkImpl; +import agent.frida.model.methods.FridaModelTargetUnloadScriptImpl; +import ghidra.dbg.target.TargetEnvironment; +import ghidra.dbg.target.TargetFocusScope; +import ghidra.dbg.target.TargetSteppable.TargetStepKindSet; +import ghidra.dbg.target.schema.*; +import ghidra.dbg.target.schema.TargetObjectSchema.ResyncMode; +import ghidra.dbg.util.PathUtils; + +@TargetObjectSchemaInfo( + name = "Thread", + attributeResync = ResyncMode.ALWAYS, + elements = { + @TargetElementType(type = Void.class) }, + attributes = { + @TargetAttributeType( + name = "Registers", + type = FridaModelTargetRegisterContainerImpl.class, + required = true, + fixed = true), + @TargetAttributeType( + name = "Stack", + type = FridaModelTargetStackImpl.class, + required = true, + fixed = true), + @TargetAttributeType(name = TargetEnvironment.ARCH_ATTRIBUTE_NAME, type = String.class), + @TargetAttributeType(type = Object.class) }) +public class FridaModelTargetThreadImpl extends FridaModelTargetObjectImpl + implements FridaModelTargetThread { + + public static final TargetStepKindSet SUPPORTED_KINDS = TargetStepKindSet.of(); /* + TargetStepKind.ADVANCE, // + TargetStepKind.FINISH, // + TargetStepKind.LINE, // + TargetStepKind.OVER, // + TargetStepKind.OVER_LINE, // + TargetStepKind.RETURN, // + TargetStepKind.UNTIL, // + TargetStepKind.EXTENDED); + */ + + protected static String indexThread(Integer id) { + return PathUtils.makeIndex(id); + } + + protected static String indexThread(FridaThread thread) { + return FridaClient.getId(thread); + } + + protected static String keyThread(FridaThread thread) { + return PathUtils.makeKey(indexThread(thread)); + } + + protected final FridaModelTargetStackImpl stack; + + private FridaModelTargetProcess process; + private Integer base = 10; + + private FridaModelTargetRegisterContainerImpl registers; + private FridaModelTargetThreadStalkImpl stalk; + private FridaModelTargetUnloadScriptImpl unload; + + public FridaModelTargetThreadImpl(FridaModelTargetThreadContainer threads, + FridaModelTargetProcess process, FridaThread thread) { + super(threads.getModel(), threads, keyThread(thread), thread, "Thread"); + this.process = process; + + this.registers = new FridaModelTargetRegisterContainerImpl(this); + this.stack = new FridaModelTargetStackImpl(this, process); + + this.stalk = new FridaModelTargetThreadStalkImpl(this); + this.unload = new FridaModelTargetUnloadScriptImpl(this, stalk.getName()); + + changeAttributes(List.of(), List.of( // + registers, + stack, // + stalk, // + unload // + ), Map.of( // + //ACCESSIBLE_ATTRIBUTE_NAME, accessible = false, // + DISPLAY_ATTRIBUTE_NAME, getDisplay(), // + STATE_ATTRIBUTE_NAME, TargetExecutionState.ALIVE // + //SUPPORTED_STEP_KINDS_ATTRIBUTE_NAME, SUPPORTED_KINDS // + ), "Initialized"); + + getManager().addStateListener(this); + getManager().addEventsListener(this); + } + + @Override + public void setModelObject(Object modelObject) { + super.setModelObject(modelObject); + getModel().addModelObject(modelObject, this); + } + + public String getDescription(int level) { + FridaThread thread = (FridaThread) getModelObject(); + return thread.getDescription(); + } + + @Override + public String getDisplay() { + String tidstr = FridaClient.getId(getThread()); + if (base == 16) { + tidstr = "0x" + tidstr; + } else { + tidstr = Long.toString(Long.parseLong(tidstr,16)); + } + return "[" + tidstr + "]"; + } + + @Override + public void threadSelected(FridaThread eventThread, FridaFrame frame, FridaCause cause) { + if (eventThread.getTid().equals(getThread().getTid())) { + ((FridaModelTargetFocusScope) searchForSuitable(TargetFocusScope.class)).setFocus(this); + } + } + + @Override + public void threadStateChangedSpecific(FridaState state, FridaReason reason) { + TargetExecutionState targetState = FridaClient.convertState(state); + changeAttributes(List.of(), List.of(), Map.of( // + STATE_ATTRIBUTE_NAME, targetState // + ), reason.desc()); + stack.threadStateChangedSpecific(state, reason); + registers.threadStateChangedSpecific(state, reason); + } + + @Override + public CompletableFuture setActive() { + return CompletableFuture.completedFuture(null); + } + + @Override + public FridaModelTargetStackImpl getStack() { + return stack; + } + + @Override + public FridaThread getThread() { + return (FridaThread) getModelObject(); + } + + public FridaModelTargetProcess getProcess() { + return process; + } + + @Override + public String getExecutingProcessorType() { + return null; //thread.getExecutingProcessorType().description; + } + + public void setBase(Object value) { + this.base = (Integer) value; + changeAttributes(List.of(), List.of(), Map.of( // + DISPLAY_ATTRIBUTE_NAME, getDisplay()// + ), "Started"); + } + + @Override + public void stateChanged(FridaState state, FridaCause cause) { + FridaModelTargetThreadContainer container = (FridaModelTargetThreadContainer) getParent(); + Reasons unknown = FridaReason.Reasons.UNKNOWN; + process.threadStateChanged(getThread(), state, cause, unknown); + container.threadStateChanged(getThread(), state, cause, unknown); + //registers.threadStateChanged(getThread(), state, cause, unknown); + threadStateChangedSpecific(state, unknown); + } + +} diff --git a/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/methods/FridaModelTargetFunctionInterceptorImpl.java b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/methods/FridaModelTargetFunctionInterceptorImpl.java new file mode 100644 index 0000000000..1e1a41ab5e --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/methods/FridaModelTargetFunctionInterceptorImpl.java @@ -0,0 +1,94 @@ +/* ### + * 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.model.methods; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.CompletableFuture; + +import agent.frida.manager.FridaExport; +import agent.frida.manager.FridaImport; +import agent.frida.manager.FridaSymbol; +import agent.frida.model.iface1.FridaModelTargetMethod; +import agent.frida.model.iface2.FridaModelTargetObject; +import agent.frida.model.impl.FridaModelTargetObjectImpl; +import ghidra.dbg.target.TargetMethod; +import ghidra.dbg.target.schema.TargetAttributeType; +import ghidra.dbg.target.schema.TargetElementType; +import ghidra.dbg.target.schema.TargetObjectSchemaInfo; + +@TargetObjectSchemaInfo( + name = "FunctionIntercept", + elements = { + @TargetElementType(type = Void.class) + }, + attributes = { + @TargetAttributeType(type = Object.class) + }, + canonicalContainer = true) +public class FridaModelTargetFunctionInterceptorImpl extends FridaModelTargetObjectImpl + implements FridaModelTargetMethod { + + protected final TargetParameterMap paramDescs; + + public FridaModelTargetFunctionInterceptorImpl(FridaModelTargetObject parent) { + super(parent.getModel(), parent, "intercept", "FunctionIntercept"); + + changeAttributes(List.of(), List.of(), Map.of( // + TargetMethod.PARAMETERS_ATTRIBUTE_NAME, + paramDescs = TargetParameterMap.copyOf(computeParameters()) // + ), "Initialized"); + } + + protected Map> computeParameters() { + HashMap> map = + new HashMap>(); + ParameterDescription onEnter = ParameterDescription.create(String.class, "OnEnter", + false, "", "onEnter file", "JS file with onEnter implemenation"); + ParameterDescription onLeave = ParameterDescription.create(String.class, "OnLeave", + false, "", "onLeave file", "JS file with onLeave implemenation"); + ParameterDescription name = ParameterDescription.create(String.class, "Name", + false, "intercept", "name", "name for future unload"); + ParameterDescription script = ParameterDescription.create(String.class, "Script", + false, "", "script", "script to execute on result"); + map.put("OnEnter", onEnter); + map.put("OnLeave", onLeave); + map.put("Name", name); + map.put("Script", script); + return map; + } + + @Override + public CompletableFuture invoke(Map arguments) { + Object modelObject = getModelObject(); + String address = null; + if (modelObject instanceof FridaImport) { + address = ((FridaImport) modelObject).getAddress(); + } + if (modelObject instanceof FridaExport) { + address = ((FridaExport) modelObject).getAddress(); + } + if (modelObject instanceof FridaSymbol) { + address = ((FridaSymbol) modelObject).getAddress(); + } + if (address != null) { + getManager().interceptFunction(address, arguments); + } + return CompletableFuture.completedFuture(null); + } + +} diff --git a/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/methods/FridaModelTargetMemoryPatchImpl.java b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/methods/FridaModelTargetMemoryPatchImpl.java new file mode 100644 index 0000000000..d3dc883b4f --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/methods/FridaModelTargetMemoryPatchImpl.java @@ -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.model.methods; + +import java.util.*; +import java.util.concurrent.CompletableFuture; + +import agent.frida.model.iface1.FridaModelTargetMethod; +import agent.frida.model.iface2.FridaModelTargetMemoryContainer; +import agent.frida.model.impl.FridaModelTargetObjectImpl; +import ghidra.dbg.target.TargetMethod; +import ghidra.dbg.target.schema.*; + +@TargetObjectSchemaInfo( + name = "MemoryPatch", + elements = { + @TargetElementType(type = Void.class) + }, + attributes = { + @TargetAttributeType(type = Object.class) + }, + canonicalContainer = true) +public class FridaModelTargetMemoryPatchImpl extends FridaModelTargetObjectImpl + implements FridaModelTargetMethod { + + protected final TargetParameterMap paramDescs; + + public FridaModelTargetMemoryPatchImpl(FridaModelTargetMemoryContainer memory) { + super(memory.getModel(), memory, "patch", "MemoryPatch"); + + changeAttributes(List.of(), List.of(), Map.of( // + TargetMethod.PARAMETERS_ATTRIBUTE_NAME, + paramDescs = TargetParameterMap.copyOf(computeParameters()) // + ), "Initialized"); + } + + protected Map> computeParameters() { + HashMap> map = + new HashMap>(); + ParameterDescription address = ParameterDescription.create(String.class, "Address", + true, "", "Address", "addres to patch"); + ParameterDescription size = ParameterDescription.create(Long.class, "Size", true, + 0L, "Size", "size of patch"); + ParameterDescription pattern = ParameterDescription.create(String.class, "Callback", + true, "", "Callback", "e.g. code => {const cw = new X86Writer(code, { pc: addr }); cw.putRet(); cw.flush();}"); + map.put("Address", address); + map.put("Size", size); + map.put("Callback", pattern); + return map; + } + + @Override + public CompletableFuture invoke(Map arguments) { + String addr = (String) arguments.get("Address"); + if (!addr.startsWith("0x")) { + addr = "0x" + addr; + } + getManager().console("Memory.patchCode(" + + "ptr(" + addr + "), " + + arguments.get("Size") + ", " + + arguments.get("Callback") + ");"); + return CompletableFuture.completedFuture(null); + } + +} diff --git a/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/methods/FridaModelTargetMemoryProtectImpl.java b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/methods/FridaModelTargetMemoryProtectImpl.java new file mode 100644 index 0000000000..c5c7c72baa --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/methods/FridaModelTargetMemoryProtectImpl.java @@ -0,0 +1,77 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.frida.model.methods; + +import java.util.*; +import java.util.concurrent.CompletableFuture; + +import agent.frida.model.iface1.FridaModelTargetMethod; +import agent.frida.model.iface2.FridaModelTargetMemoryContainer; +import agent.frida.model.impl.FridaModelTargetObjectImpl; +import ghidra.dbg.target.TargetMethod; +import ghidra.dbg.target.schema.*; + +@TargetObjectSchemaInfo( + name = "MemoryProtect", + elements = { + @TargetElementType(type = Void.class) + }, + attributes = { + @TargetAttributeType(type = Object.class) + }, + canonicalContainer = true) +public class FridaModelTargetMemoryProtectImpl extends FridaModelTargetObjectImpl + implements FridaModelTargetMethod { + + protected final TargetParameterMap paramDescs; + private boolean kernel; + + public FridaModelTargetMemoryProtectImpl(FridaModelTargetMemoryContainer memory, boolean kernel) { + super(memory.getModel(), memory, "protect", "MemoryProtect"); + this.kernel = kernel; + + changeAttributes(List.of(), List.of(), Map.of( // + TargetMethod.PARAMETERS_ATTRIBUTE_NAME, + paramDescs = TargetParameterMap.copyOf(computeParameters()) // + ), "Initialized"); + } + + protected Map> computeParameters() { + HashMap> map = + new HashMap>(); + ParameterDescription address = ParameterDescription.create(String.class, "Address", + true, "", "Address", "starting address"); + ParameterDescription size = ParameterDescription.create(Long.class, "Size", true, + 4096L, "Size", "size to protect"); + ParameterDescription protection = ParameterDescription.create(String.class, "Prot", + true, "", "Protection", "e.g. r-x"); + map.put("Address", address); + map.put("Size", size); + map.put("Prot", protection); + return map; + } + + @Override + public CompletableFuture invoke(Map arguments) { + String cmd = kernel ? "Kernel" : "Memory"; + getManager().console(cmd + ".protect(" + + "ptr(0x" + arguments.get("Address") + "), " + + arguments.get("Size") + ", " + + "'" + arguments.get("Prot") + "');"); + return CompletableFuture.completedFuture(null); + } + +} diff --git a/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/methods/FridaModelTargetMemoryScanImpl.java b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/methods/FridaModelTargetMemoryScanImpl.java new file mode 100644 index 0000000000..2e0a369bd3 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/methods/FridaModelTargetMemoryScanImpl.java @@ -0,0 +1,100 @@ +/* ### + * 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.model.methods; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.CompletableFuture; + +import agent.frida.model.iface1.FridaModelTargetMethod; +import agent.frida.model.iface2.FridaModelTargetMemoryContainer; +import agent.frida.model.impl.FridaModelTargetObjectImpl; +import ghidra.dbg.target.TargetMethod; +import ghidra.dbg.target.schema.TargetAttributeType; +import ghidra.dbg.target.schema.TargetElementType; +import ghidra.dbg.target.schema.TargetObjectSchemaInfo; + +@TargetObjectSchemaInfo( + name = "MemoryScan", + elements = { + @TargetElementType(type = Void.class) + }, + attributes = { + @TargetAttributeType(type = Object.class) + }, + canonicalContainer = true) +public class FridaModelTargetMemoryScanImpl extends FridaModelTargetObjectImpl + implements FridaModelTargetMethod { + + protected final TargetParameterMap paramDescs; + private boolean kernel; + + public FridaModelTargetMemoryScanImpl(FridaModelTargetMemoryContainer memory, boolean kernel) { + super(memory.getModel(), memory, "scan", "MemoryScan"); + this.kernel = kernel; + + changeAttributes(List.of(), List.of(), Map.of( // + TargetMethod.PARAMETERS_ATTRIBUTE_NAME, + paramDescs = TargetParameterMap.copyOf(computeParameters()) // + ), "Initialized"); + } + + protected Map> computeParameters() { + HashMap> map = + new HashMap>(); + ParameterDescription address = ParameterDescription.create(String.class, "Address", + true, "", "Address", "starting address"); + ParameterDescription size = ParameterDescription.create(Long.class, "Size", true, + 0x1000L, "Size", "size to scan"); + ParameterDescription pattern = ParameterDescription.create(String.class, "Pattern", + true, "", "Pattern", "e.g. DE AD ?? BE EF"); + ParameterDescription stopOnMatch = ParameterDescription.create(Boolean.class, "Stop", + true, true, "Stop on match", "stop on match"); + ParameterDescription script = ParameterDescription.create(String.class, "Script", + false, "", "script", "script to execute on result"); + map.put("Address", address); + map.put("Size", size); + map.put("Pattern", pattern); + map.put("Stop", stopOnMatch); + map.put("Script", script); + return map; + } + + @Override + public CompletableFuture invoke(Map arguments) { + String ret = (boolean) arguments.get("Stop") ? " return 'stop'; " : ""; + String addr = (String) arguments.get("Address"); + if (!addr.startsWith("0x")) { + addr = "0x" + addr; + } + String cmd = kernel ? "Kernel" : "Memory"; + getManager().console("result = " + cmd + ".scan(" + + "ptr(" + addr + "), " + + arguments.get("Size") + ", " + + "'" + arguments.get("Pattern") + "', " + + "{ onMatch(address, size) { " + + " console.log('Found match at ', address); " + + ret + + " }," + + " onComplete() { " + + " console.log('scan complete'); " + + " } " + + "});"); + return CompletableFuture.completedFuture(null); + } + +} diff --git a/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/methods/FridaModelTargetMemoryWatchImpl.java b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/methods/FridaModelTargetMemoryWatchImpl.java new file mode 100644 index 0000000000..f7a6ae5c6f --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/methods/FridaModelTargetMemoryWatchImpl.java @@ -0,0 +1,81 @@ +/* ### + * 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.model.methods; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.CompletableFuture; + +import agent.frida.model.iface1.FridaModelTargetMethod; +import agent.frida.model.iface2.FridaModelTargetMemoryContainer; +import agent.frida.model.impl.FridaModelTargetObjectImpl; +import ghidra.dbg.target.TargetMethod; +import ghidra.dbg.target.schema.TargetAttributeType; +import ghidra.dbg.target.schema.TargetElementType; +import ghidra.dbg.target.schema.TargetObjectSchemaInfo; + +@TargetObjectSchemaInfo( + name = "MemoryWatch", + elements = { + @TargetElementType(type = Void.class) + }, + attributes = { + @TargetAttributeType(type = Object.class) + }, + canonicalContainer = true) +public class FridaModelTargetMemoryWatchImpl extends FridaModelTargetObjectImpl + implements FridaModelTargetMethod { + + protected final TargetParameterMap paramDescs; + + public FridaModelTargetMemoryWatchImpl(FridaModelTargetMemoryContainer memory) { + super(memory.getModel(), memory, "watch", "MemoryWatch"); + + changeAttributes(List.of(), List.of(), Map.of( // + TargetMethod.PARAMETERS_ATTRIBUTE_NAME, + paramDescs = TargetParameterMap.copyOf(computeParameters()) // + ), "Initialized"); + } + + protected Map> computeParameters() { + HashMap> map = + new HashMap>(); + ParameterDescription address = ParameterDescription.create(String.class, "Address", + true, "", "Address", "starting address"); + ParameterDescription size = ParameterDescription.create(Long.class, "Size", true, + 1L, "Size", "size to scan"); + ParameterDescription onAccess = ParameterDescription.create(String.class, "OnAccess", + true, "", "onAccess file", "JS file with onAccess implemenation"); + ParameterDescription name = ParameterDescription.create(String.class, "Name", + false, "watch", "name", "name for future unload"); + ParameterDescription script = ParameterDescription.create(String.class, "Script", + false, "", "script", "script to execute on result"); + map.put("Address", address); + map.put("Size", size); + map.put("OnAccess", onAccess); + map.put("Name", name); + map.put("Script", script); + return map; + } + + @Override + public CompletableFuture invoke(Map arguments) { + getManager().watchMemory(arguments); + return CompletableFuture.completedFuture(null); + } + +} diff --git a/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/methods/FridaModelTargetModuleInitImpl.java b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/methods/FridaModelTargetModuleInitImpl.java new file mode 100644 index 0000000000..03119f857d --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/methods/FridaModelTargetModuleInitImpl.java @@ -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.model.methods; + +import java.util.*; +import java.util.concurrent.CompletableFuture; + +import agent.frida.model.iface1.FridaModelTargetMethod; +import agent.frida.model.iface2.FridaModelTargetModuleContainer; +import agent.frida.model.impl.FridaModelTargetObjectImpl; +import ghidra.dbg.target.TargetMethod; +import ghidra.dbg.target.schema.*; + +@TargetObjectSchemaInfo( + name = "ModuleInit", + elements = { + @TargetElementType(type = Void.class) + }, + attributes = { + @TargetAttributeType(type = Object.class) + }, + canonicalContainer = true) +public class FridaModelTargetModuleInitImpl extends FridaModelTargetObjectImpl + implements FridaModelTargetMethod { + + protected final TargetParameterMap paramDescs; + + public FridaModelTargetModuleInitImpl(FridaModelTargetModuleContainer modules) { + super(modules.getModel(), modules, "init", "ModuleInit"); + + changeAttributes(List.of(), List.of(), Map.of( // + TargetMethod.PARAMETERS_ATTRIBUTE_NAME, + paramDescs = TargetParameterMap.copyOf(computeParameters()) // + ), "Initialized"); + } + + protected Map> computeParameters() { + HashMap> map = + new HashMap>(); + ParameterDescription fpath = ParameterDescription.create(String.class, "Name", + true, "", "Name", "module name"); + map.put("Name", fpath); + return map; + } + + @Override + public CompletableFuture invoke(Map arguments) { + getManager().console("Module.enusureInitialized(" + + "'" + arguments.get("Name") + "');"); + return CompletableFuture.completedFuture(null); + } + +} diff --git a/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/methods/FridaModelTargetModuleInterceptorImpl.java b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/methods/FridaModelTargetModuleInterceptorImpl.java new file mode 100644 index 0000000000..7739457611 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/methods/FridaModelTargetModuleInterceptorImpl.java @@ -0,0 +1,85 @@ +/* ### + * 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.model.methods; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.CompletableFuture; + +import agent.frida.model.iface1.FridaModelTargetMethod; +import agent.frida.model.iface2.FridaModelTargetObject; +import agent.frida.model.impl.FridaModelTargetObjectImpl; +import ghidra.dbg.target.TargetMethod; +import ghidra.dbg.target.schema.TargetAttributeType; +import ghidra.dbg.target.schema.TargetElementType; +import ghidra.dbg.target.schema.TargetObjectSchemaInfo; + +@TargetObjectSchemaInfo( + name = "ModuleIntercept", + elements = { + @TargetElementType(type = Void.class) + }, + attributes = { + @TargetAttributeType(type = Object.class) + }, + canonicalContainer = true) +public class FridaModelTargetModuleInterceptorImpl extends FridaModelTargetObjectImpl + implements FridaModelTargetMethod { + + protected final TargetParameterMap paramDescs; + + public FridaModelTargetModuleInterceptorImpl(FridaModelTargetObject parent) { + super(parent.getModel(), parent, "intercept", "ModuleIntercept"); + + changeAttributes(List.of(), List.of(), Map.of( // + TargetMethod.PARAMETERS_ATTRIBUTE_NAME, + paramDescs = TargetParameterMap.copyOf(computeParameters()) // + ), "Initialized"); + } + + protected Map> computeParameters() { + HashMap> map = + new HashMap>(); + ParameterDescription address = ParameterDescription.create(String.class, "Address", + true, "", "Address", "function address"); + ParameterDescription onEnter = ParameterDescription.create(String.class, "OnEnter", + true, "", "onEnter file", "JS file with onEnter implemenation"); + ParameterDescription onLeave = ParameterDescription.create(String.class, "OnLeave", + true, "", "onLeave file", "JS file with onLeave implemenation"); + ParameterDescription name = ParameterDescription.create(String.class, "Name", + false, "intercept", "name", "name for future unload"); + ParameterDescription script = ParameterDescription.create(String.class, "Script", + false, "", "script", "script to execute on result"); + map.put("Address", address); + map.put("OnEnter", onEnter); + map.put("OnLeave", onLeave); + map.put("Name", name); + map.put("Script", script); + return map; + } + + @Override + public CompletableFuture invoke(Map arguments) { + String address = (String) arguments.get("Address"); + if (!address.startsWith("0x")) { + address = "0x" + address; + } + getManager().interceptFunction(address, arguments); + return CompletableFuture.completedFuture(null); + } + +} diff --git a/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/methods/FridaModelTargetModuleLoadImpl.java b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/methods/FridaModelTargetModuleLoadImpl.java new file mode 100644 index 0000000000..68bac5cc5b --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/methods/FridaModelTargetModuleLoadImpl.java @@ -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.model.methods; + +import java.util.*; +import java.util.concurrent.CompletableFuture; + +import agent.frida.model.iface1.FridaModelTargetMethod; +import agent.frida.model.iface2.FridaModelTargetModuleContainer; +import agent.frida.model.impl.FridaModelTargetObjectImpl; +import ghidra.dbg.target.TargetMethod; +import ghidra.dbg.target.schema.*; + +@TargetObjectSchemaInfo( + name = "ModuleLoad", + elements = { + @TargetElementType(type = Void.class) + }, + attributes = { + @TargetAttributeType(type = Object.class) + }, + canonicalContainer = true) +public class FridaModelTargetModuleLoadImpl extends FridaModelTargetObjectImpl + implements FridaModelTargetMethod { + + protected final TargetParameterMap paramDescs; + + public FridaModelTargetModuleLoadImpl(FridaModelTargetModuleContainer modules) { + super(modules.getModel(), modules, "load", "ModuleLoad"); + + changeAttributes(List.of(), List.of(), Map.of( // + TargetMethod.PARAMETERS_ATTRIBUTE_NAME, + paramDescs = TargetParameterMap.copyOf(computeParameters()) // + ), "Initialized"); + } + + protected Map> computeParameters() { + HashMap> map = + new HashMap>(); + ParameterDescription fpath = ParameterDescription.create(String.class, "Path", + true, "", "Path", "filesystem path"); + map.put("Path", fpath); + return map; + } + + @Override + public CompletableFuture invoke(Map arguments) { + getManager().console("Module.load(" + + "'" + arguments.get("Path") + "');"); + return CompletableFuture.completedFuture(null); + } + +} diff --git a/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/methods/FridaModelTargetSymbolFromAddressImpl.java b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/methods/FridaModelTargetSymbolFromAddressImpl.java new file mode 100644 index 0000000000..391a836ae1 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/methods/FridaModelTargetSymbolFromAddressImpl.java @@ -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.model.methods; + +import java.util.*; +import java.util.concurrent.CompletableFuture; + +import agent.frida.model.iface1.FridaModelTargetMethod; +import agent.frida.model.iface2.FridaModelTargetSymbolContainer; +import agent.frida.model.impl.FridaModelTargetObjectImpl; +import ghidra.dbg.target.TargetMethod; +import ghidra.dbg.target.schema.*; + +@TargetObjectSchemaInfo( + name = "SymbolFromAddress", + elements = { + @TargetElementType(type = Void.class) + }, + attributes = { + @TargetAttributeType(type = Object.class) + }, + canonicalContainer = true) +public class FridaModelTargetSymbolFromAddressImpl extends FridaModelTargetObjectImpl + implements FridaModelTargetMethod { + + protected final TargetParameterMap paramDescs; + + public FridaModelTargetSymbolFromAddressImpl(FridaModelTargetSymbolContainer symbols) { + super(symbols.getModel(), symbols, "fromAddress", "SymbolFromAddress"); + + changeAttributes(List.of(), List.of(), Map.of( // + TargetMethod.PARAMETERS_ATTRIBUTE_NAME, + paramDescs = TargetParameterMap.copyOf(computeParameters()) // + ), "Initialized"); + } + + protected Map> computeParameters() { + HashMap> map = + new HashMap>(); + ParameterDescription address = ParameterDescription.create(String.class, "Address", + true, "", "Address", "starting address"); + map.put("Address", address); + return map; + } + + @Override + public CompletableFuture invoke(Map arguments) { + String addr = (String) arguments.get("Address"); + if (!addr.startsWith("0x")) { + addr = "0x" + addr; + } + getManager().console("DebugSymbol.fromAddress(" + + "ptr(" + addr + "));"); + return CompletableFuture.completedFuture(null); + } + +} diff --git a/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/methods/FridaModelTargetSymbolFromNameImpl.java b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/methods/FridaModelTargetSymbolFromNameImpl.java new file mode 100644 index 0000000000..68cd8cbf80 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/methods/FridaModelTargetSymbolFromNameImpl.java @@ -0,0 +1,67 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package agent.frida.model.methods; + +import java.util.*; +import java.util.concurrent.CompletableFuture; + +import agent.frida.model.iface1.FridaModelTargetMethod; +import agent.frida.model.iface2.FridaModelTargetSymbolContainer; +import agent.frida.model.impl.FridaModelTargetObjectImpl; +import ghidra.dbg.target.TargetMethod; +import ghidra.dbg.target.schema.*; + +@TargetObjectSchemaInfo( + name = "SymbolFromName", + elements = { + @TargetElementType(type = Void.class) + }, + attributes = { + @TargetAttributeType(type = Object.class) + }, + canonicalContainer = true) +public class FridaModelTargetSymbolFromNameImpl extends FridaModelTargetObjectImpl + implements FridaModelTargetMethod { + + protected final TargetParameterMap paramDescs; + + public FridaModelTargetSymbolFromNameImpl(FridaModelTargetSymbolContainer symbols) { + super(symbols.getModel(), symbols, "fromName", "SymbolFromName"); + + changeAttributes(List.of(), List.of(), Map.of( // + TargetMethod.PARAMETERS_ATTRIBUTE_NAME, + paramDescs = TargetParameterMap.copyOf(computeParameters()) // + ), "Initialized"); + } + + protected Map> computeParameters() { + HashMap> map = + new HashMap>(); + ParameterDescription name = ParameterDescription.create(String.class, "Name", + true, "", "Name", "symbol name"); + map.put("Name", name); + return map; + } + + @Override + public CompletableFuture invoke(Map arguments) { + String name = (String) arguments.get("Name"); + getManager().console("DebugSymbol.fromName(" + + name + ");"); + return CompletableFuture.completedFuture(null); + } + +} diff --git a/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/methods/FridaModelTargetSymbolLoadImpl.java b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/methods/FridaModelTargetSymbolLoadImpl.java new file mode 100644 index 0000000000..d9bff53689 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/methods/FridaModelTargetSymbolLoadImpl.java @@ -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.model.methods; + +import java.util.*; +import java.util.concurrent.CompletableFuture; + +import agent.frida.model.iface1.FridaModelTargetMethod; +import agent.frida.model.iface2.FridaModelTargetSymbolContainer; +import agent.frida.model.impl.FridaModelTargetObjectImpl; +import ghidra.dbg.target.TargetMethod; +import ghidra.dbg.target.schema.*; + +@TargetObjectSchemaInfo( + name = "SymbolLoad", + elements = { + @TargetElementType(type = Void.class) + }, + attributes = { + @TargetAttributeType(type = Object.class) + }, + canonicalContainer = true) +public class FridaModelTargetSymbolLoadImpl extends FridaModelTargetObjectImpl + implements FridaModelTargetMethod { + + protected final TargetParameterMap paramDescs; + + public FridaModelTargetSymbolLoadImpl(FridaModelTargetSymbolContainer modules) { + super(modules.getModel(), modules, "load", "SymbolLoad"); + + changeAttributes(List.of(), List.of(), Map.of( // + TargetMethod.PARAMETERS_ATTRIBUTE_NAME, + paramDescs = TargetParameterMap.copyOf(computeParameters()) // + ), "Initialized"); + } + + protected Map> computeParameters() { + HashMap> map = + new HashMap>(); + ParameterDescription fpath = ParameterDescription.create(String.class, "Path", + true, "", "Path", "filesystem path"); + map.put("Path", fpath); + return map; + } + + @Override + public CompletableFuture invoke(Map arguments) { + getManager().console("DebugSymbol.load(" + + "'" + arguments.get("Path") + "');"); + return CompletableFuture.completedFuture(null); + } + +} diff --git a/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/methods/FridaModelTargetThreadSleepImpl.java b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/methods/FridaModelTargetThreadSleepImpl.java new file mode 100644 index 0000000000..454a7190fd --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/methods/FridaModelTargetThreadSleepImpl.java @@ -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.model.methods; + +import java.util.*; +import java.util.concurrent.CompletableFuture; + +import agent.frida.model.iface1.FridaModelTargetMethod; +import agent.frida.model.impl.FridaModelTargetObjectImpl; +import agent.frida.model.impl.FridaModelTargetThreadContainerImpl; +import ghidra.dbg.target.TargetMethod; +import ghidra.dbg.target.schema.*; + +@TargetObjectSchemaInfo( + name = "ThreadSleep", + elements = { + @TargetElementType(type = Void.class) + }, + attributes = { + @TargetAttributeType(type = Void.class) + }, + canonicalContainer = true) +public class FridaModelTargetThreadSleepImpl extends FridaModelTargetObjectImpl + implements FridaModelTargetMethod { + + protected final TargetParameterMap paramDescs; + + public FridaModelTargetThreadSleepImpl(FridaModelTargetThreadContainerImpl threads) { + super(threads.getModel(), threads, "sleep", "ThreadSleep"); + + changeAttributes(List.of(), List.of(), Map.of( // + TargetMethod.PARAMETERS_ATTRIBUTE_NAME, + paramDescs = TargetParameterMap.copyOf(computeParameters()) // + ), "Initialized"); + } + + protected Map> computeParameters() { + HashMap> map = + new HashMap>(); + ParameterDescription fpath = ParameterDescription.create(Long.class, "Delay", + true, 100L, "Delay", "sleep for current thread (seconds)"); + map.put("Delay", fpath); + return map; + } + + @Override + public CompletableFuture invoke(Map arguments) { + getManager().console("Thread.sleep(" + + "'" + arguments.get("Delay") + "');"); + return CompletableFuture.completedFuture(null); + } + +} diff --git a/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/methods/FridaModelTargetThreadStalkImpl.java b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/methods/FridaModelTargetThreadStalkImpl.java new file mode 100644 index 0000000000..8e5b397e93 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/methods/FridaModelTargetThreadStalkImpl.java @@ -0,0 +1,120 @@ +/* ### + * 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.model.methods; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.CompletableFuture; + +import agent.frida.manager.FridaThread; +import agent.frida.model.iface1.FridaModelTargetMethod; +import agent.frida.model.iface2.FridaModelTargetThreadContainer; +import agent.frida.model.impl.FridaModelTargetObjectImpl; +import agent.frida.model.impl.FridaModelTargetThreadImpl; +import ghidra.dbg.target.TargetMethod; +import ghidra.dbg.target.schema.TargetAttributeType; +import ghidra.dbg.target.schema.TargetElementType; +import ghidra.dbg.target.schema.TargetObjectSchemaInfo; + +@TargetObjectSchemaInfo( + name = "ThreadStalk", + elements = { + @TargetElementType(type = Void.class) + }, + attributes = { + @TargetAttributeType(type = Object.class) + }, + canonicalContainer = true) +public class FridaModelTargetThreadStalkImpl extends FridaModelTargetObjectImpl + implements FridaModelTargetMethod { + + protected final TargetParameterMap paramDescs; + private boolean useCurrentThread; + + public FridaModelTargetThreadStalkImpl(FridaModelTargetThreadContainer threads) { + super(threads.getModel(), threads, "stalk", "ThreadStalk"); + useCurrentThread = true; + + changeAttributes(List.of(), List.of(), Map.of( // + TargetMethod.PARAMETERS_ATTRIBUTE_NAME, + paramDescs = TargetParameterMap.copyOf(computeParameters()) // + ), "Initialized"); + } + + public FridaModelTargetThreadStalkImpl(FridaModelTargetThreadImpl thread) { + super(thread.getModel(), thread, "stalk", "ThreadStalk"); + useCurrentThread = false; + + changeAttributes(List.of(), List.of(), Map.of( // + TargetMethod.PARAMETERS_ATTRIBUTE_NAME, + paramDescs = TargetParameterMap.copyOf(computeParameters()) // + ), "Initialized"); + } + + public String getTid() { + Long tid = 0L; + if (useCurrentThread) { + FridaThread currentThread = getManager().getCurrentThread(); + if (currentThread != null) { + tid = currentThread.getTid(); + } + } else { + tid = ((FridaThread) getModelObject()).getTid(); + } + return Long.toString(tid); + } + + public Map> computeParameters() { + HashMap> map = + new HashMap>(); + ParameterDescription e_calls = ParameterDescription.create(Boolean.class, "EventCall", + true, true, "event=call", "CALL instruction"); + ParameterDescription e_ret = ParameterDescription.create(Boolean.class, "EventRet", + true, false, "event=ret", "RET instructions"); + ParameterDescription e_exec = ParameterDescription.create(Boolean.class, "EventExec", + true, false, "event=exec", "all instructions (not recommended)"); + ParameterDescription e_block = ParameterDescription.create(Boolean.class, "EventBlock", + true, false, "event=block", "block executed"); + ParameterDescription e_compile = ParameterDescription.create(Boolean.class, "EventCompile", + true, false, "event=compile", "block compiled"); + ParameterDescription onReceive = ParameterDescription.create(String.class, "OnReceive", + false, "", "onRecv file", "JS file with onReceive implemenation"); + ParameterDescription onCallSummary = ParameterDescription.create(String.class, "OnCallSummary", + false, "", "onCall file", "JS file with onCallSummary implementation"); + ParameterDescription name = ParameterDescription.create(String.class, "Name", + false, "stalk", "name", "name for future unload"); + ParameterDescription script = ParameterDescription.create(String.class, "Script", + false, "", "script", "script to execute on result"); + map.put("EventCall", e_calls); + map.put("EventRet", e_ret); + map.put("EventExec", e_exec); + map.put("EventBlock", e_block); + map.put("EventCompile", e_compile); + map.put("OnReceive", onReceive); + map.put("OnCallSummary", onCallSummary); + map.put("Name", name); + map.put("Script", script); + return map; + } + + @Override + public CompletableFuture invoke(Map arguments) { + getManager().stalkThread(getTid(), arguments); + return CompletableFuture.completedFuture(null); + } + +} diff --git a/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/methods/FridaModelTargetUnloadScriptImpl.java b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/methods/FridaModelTargetUnloadScriptImpl.java new file mode 100644 index 0000000000..fd66b4bc31 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-frida/src/main/java/agent/frida/model/methods/FridaModelTargetUnloadScriptImpl.java @@ -0,0 +1,71 @@ +/* ### + * 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.model.methods; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.CompletableFuture; + +import agent.frida.model.iface1.FridaModelTargetMethod; +import agent.frida.model.iface2.FridaModelTargetObject; +import agent.frida.model.impl.FridaModelTargetObjectImpl; +import ghidra.dbg.target.TargetMethod; +import ghidra.dbg.target.schema.TargetAttributeType; +import ghidra.dbg.target.schema.TargetElementType; +import ghidra.dbg.target.schema.TargetObjectSchemaInfo; + +@TargetObjectSchemaInfo( + name = "UnloadScript", + elements = { + @TargetElementType(type = Void.class) + }, + attributes = { + @TargetAttributeType(type = Void.class) + }, + canonicalContainer = true) +public class FridaModelTargetUnloadScriptImpl extends FridaModelTargetObjectImpl + implements FridaModelTargetMethod { + + protected final TargetParameterMap paramDescs; + private String target; + + public FridaModelTargetUnloadScriptImpl(FridaModelTargetObject object, String target) { + super(object.getModel(), object, "unload", "UnloadScript"); + this.target = target; + + changeAttributes(List.of(), List.of(), Map.of( // + TargetMethod.PARAMETERS_ATTRIBUTE_NAME, + paramDescs = TargetParameterMap.copyOf(computeParameters()) // + ), "Initialized"); + } + + protected Map> computeParameters() { + HashMap> map = + new HashMap>(); + ParameterDescription scriptName = ParameterDescription.create(String.class, "Name", + true, target, "Script", "script to be unloaded"); + map.put("Name", scriptName); + return map; + } + + @Override + public CompletableFuture invoke(Map arguments) { + getManager().unloadPermanentScript((String) arguments.get("Name")); + return CompletableFuture.completedFuture(null); + } + +} diff --git a/Ghidra/Debug/Debugger-agent-frida/src/test/java/agent/frida/model/AbstractFridaModelHost.java b/Ghidra/Debug/Debugger-agent-frida/src/test/java/agent/frida/model/AbstractFridaModelHost.java new file mode 100644 index 0000000000..574dbf169e --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-frida/src/test/java/agent/frida/model/AbstractFridaModelHost.java @@ -0,0 +1,27 @@ +/* ### + * 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.model; + +import java.util.Map; + +import ghidra.dbg.test.AbstractModelHost; + +public abstract class AbstractFridaModelHost extends AbstractModelHost { + @Override + public Map getFactoryOptions() { + return Map.ofEntries(); + } +} diff --git a/Ghidra/Debug/Debugger-agent-frida/src/test/java/agent/frida/model/AbstractModelForFridaActivationTest.java b/Ghidra/Debug/Debugger-agent-frida/src/test/java/agent/frida/model/AbstractModelForFridaActivationTest.java new file mode 100644 index 0000000000..896e56c562 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-frida/src/test/java/agent/frida/model/AbstractModelForFridaActivationTest.java @@ -0,0 +1,113 @@ +/* ### + * 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.model; + +import static org.junit.Assume.*; + +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; + +import org.junit.Test; + +import generic.Unique; +import ghidra.dbg.target.*; +import ghidra.dbg.test.AbstractDebuggerModelActivationTest; +import ghidra.dbg.util.PathPattern; + +/** + * Test model object activation and focus + * + *

+ * Activation and focus are related but separate concepts. Focus is a little looser, and is allowed + * by the model to exactly match the client's notion of focus, usually indicating the object of the + * user's interest. Activation, however, commands the model to make the given object the "current" + * object. This implies any commands issued to the CLI will affect the active object. The model + * reflects the active object back to the client via focus. This allows the model and client to + * synchronize their "active" objects, while reducing the likelihood of event feedback loops. + * Furthermore, not every object can be activated. For example, activating a register will likely + * result in the containing thread or frame becoming active instead. Or, activating a thread may + * result in its innermost frame becoming active as well. + */ +public abstract class AbstractModelForFridaActivationTest extends AbstractDebuggerModelActivationTest { + + @Test + public void testDefaultFocusIsAsExpected() throws Throwable { + List expectedDefaultFocus = getExpectedDefaultActivePath(); + assumeNotNull(expectedDefaultFocus); + m.build(); + + PathPattern pathPattern = new PathPattern(expectedDefaultFocus); + Set activatable = getActivatableThings(); + // The default must be one of the activatable objects + TargetObject obj = Unique.assertOne(activatable.stream() + .filter(f -> pathPattern.matches(f.getPath())) + .collect(Collectors.toList())); + if (m.hasInterpreter()) { + TargetInterpreter interpreter = findInterpreter(); + assertActiveViaInterpreter(obj, interpreter); + } + } + + @Test + public void testActivateEachOnce() throws Throwable { + m.build(); + + TargetActiveScope activeScope = findActiveScope(); + Set activatable = getActivatableThings(); + for (TargetObject obj : activatable) { + waitOn(activeScope.requestActivation(obj)); + if (m.hasInterpreter()) { + TargetInterpreter interpreter = findInterpreter(); + assertActiveViaInterpreter(obj, interpreter); + } + } + + } + + @Test + public void testActivateEachTwice() throws Throwable { + m.build(); + + TargetActiveScope activeScope = findActiveScope(); + Set activatable = getActivatableThings(); + for (TargetObject obj : activatable) { + waitOn(activeScope.requestActivation(obj)); + if (m.hasInterpreter()) { + TargetInterpreter interpreter = findInterpreter(); + assertActiveViaInterpreter(obj, interpreter); + } + waitOn(activeScope.requestActivation(obj)); + if (m.hasInterpreter()) { + TargetInterpreter interpreter = findInterpreter(); + assertActiveViaInterpreter(obj, interpreter); + } + } + } + + @Test + public void testActivateEachViaInterpreter() throws Throwable { + assumeTrue(m.hasInterpreter()); + m.build(); + + Set activatable = getActivatableThings(); + TargetInterpreter interpreter = findInterpreter(); + for (TargetObject obj : activatable) { + activateViaInterpreter(obj, interpreter); + assertActiveViaInterpreter(obj, interpreter); + } + } +} diff --git a/Ghidra/Debug/Debugger-agent-frida/src/test/java/agent/frida/model/AbstractModelForFridaFactoryTest.java b/Ghidra/Debug/Debugger-agent-frida/src/test/java/agent/frida/model/AbstractModelForFridaFactoryTest.java new file mode 100644 index 0000000000..4ac0c52009 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-frida/src/test/java/agent/frida/model/AbstractModelForFridaFactoryTest.java @@ -0,0 +1,27 @@ +/* ### + * 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.model; + +import java.util.Map; + +import ghidra.dbg.test.AbstractDebuggerModelFactoryTest; + +public abstract class AbstractModelForFridaFactoryTest extends AbstractDebuggerModelFactoryTest { + @Override + protected Map getFailingFactoryOptions() { + return Map.ofEntries(); + } +} diff --git a/Ghidra/Debug/Debugger-agent-frida/src/test/java/agent/frida/model/AbstractModelForFridaInterpreterTest.java b/Ghidra/Debug/Debugger-agent-frida/src/test/java/agent/frida/model/AbstractModelForFridaInterpreterTest.java new file mode 100644 index 0000000000..ae3b286bff --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-frida/src/test/java/agent/frida/model/AbstractModelForFridaInterpreterTest.java @@ -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.model; + +import java.util.List; + +import ghidra.dbg.target.TargetProcess; +import ghidra.dbg.test.*; +import ghidra.dbg.util.PathUtils; + +public abstract class AbstractModelForFridaInterpreterTest + extends AbstractDebuggerModelInterpreterTest + implements ProvidesTargetViaLaunchSpecimen { + + @Override + public AbstractDebuggerModelTest getTest() { + return this; + } + + @Override + protected void ensureInterpreterAvailable() throws Throwable { + obtainTarget(); + } + + @Override + protected List seedPath() { + return List.of(); + //return PathUtils.parse("Sessions[1]"); + } + + @Override + public List getExpectedInterpreterPath() { + return PathUtils.parse("Sessions"); + } + + @Override + protected String getEchoCommand(String msg) { + return "result = '" + msg + "';"; + } + + @Override + protected String getQuitCommand() { + return "quit"; + } + + @Override + protected String getAttachCommand() { + return "process attach " + Long.toHexString(dummy.pid); + } + + @Override + protected String getDetachCommand(TargetProcess process) { + return "process detach"; + } + + @Override + protected String getKillCommand(TargetProcess process) { + return "kill"; + } + + @Override + public DebuggerTestSpecimen getAttachSpecimen() { + return FridaLinuxSpecimen.SPIN_STRIPPED; + } + + @Override + public DebuggerTestSpecimen getLaunchSpecimen() { + return FridaLinuxSpecimen.PRINT; + } + +} diff --git a/Ghidra/Debug/Debugger-agent-frida/src/test/java/agent/frida/model/AbstractModelForFridaMethodsTest.java b/Ghidra/Debug/Debugger-agent-frida/src/test/java/agent/frida/model/AbstractModelForFridaMethodsTest.java new file mode 100644 index 0000000000..eafda4f745 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-frida/src/test/java/agent/frida/model/AbstractModelForFridaMethodsTest.java @@ -0,0 +1,296 @@ +/* ### + * 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.model; + +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; +import static org.junit.Assume.assumeTrue; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.NavigableMap; +import java.util.TreeMap; + +import org.junit.Test; + +import agent.frida.manager.FridaEventsListenerAdapter; +import agent.frida.model.iface2.FridaModelTargetProcess; +import agent.frida.model.iface2.FridaModelTargetSymbol; +import agent.frida.model.impl.FridaModelTargetMemoryContainerImpl; +import agent.frida.model.impl.FridaModelTargetThreadContainerImpl; +import agent.frida.model.impl.FridaModelTargetThreadImpl; +import agent.frida.model.methods.FridaModelTargetFunctionInterceptorImpl; +import agent.frida.model.methods.FridaModelTargetMemoryScanImpl; +import agent.frida.model.methods.FridaModelTargetMemoryWatchImpl; +import agent.frida.model.methods.FridaModelTargetThreadStalkImpl; +import generic.jar.ResourceFile; +import ghidra.dbg.DebugModelConventions; +import ghidra.dbg.DebugModelConventions.AsyncState; +import ghidra.dbg.target.TargetExecutionStateful; +import ghidra.dbg.target.TargetExecutionStateful.TargetExecutionState; +import ghidra.dbg.target.TargetKillable; +import ghidra.dbg.target.TargetLauncher; +import ghidra.dbg.target.TargetModule; +import ghidra.dbg.target.TargetModuleContainer; +import ghidra.dbg.target.TargetObject; +import ghidra.dbg.target.TargetProcess; +import ghidra.dbg.target.TargetResumable; +import ghidra.dbg.target.TargetSymbol; +import ghidra.dbg.target.TargetSymbolNamespace; +import ghidra.dbg.test.AbstractDebuggerModelTest; +import ghidra.dbg.test.RequiresLaunchSpecimen; +import ghidra.dbg.util.PathUtils; +import ghidra.framework.Application; +import ghidra.program.model.address.Address; + +public abstract class AbstractModelForFridaMethodsTest extends AbstractDebuggerModelTest + implements RequiresLaunchSpecimen { + + protected NavigableMap symbolsByKey = new TreeMap<>(); + + @Override + public FridaLinuxSpecimen getLaunchSpecimen() { + return FridaLinuxSpecimen.PRINT; + } + public FridaLinuxSpecimen getPrintSpecimen() { + return FridaLinuxSpecimen.PRINT; + } + public FridaLinuxSpecimen getStackSpecimen() { + return FridaLinuxSpecimen.STACK; + } + public FridaLinuxSpecimen getSpinSpecimen() { + return FridaLinuxSpecimen.SPIN_STRIPPED; + } + + protected TargetProcess runTestLaunch(DebuggerTestSpecimen specimen, TargetLauncher launcher) throws Throwable { + waitAcc(launcher); + waitOn(launcher.launch(specimen.getLauncherArgs())); + + TargetProcess process = retryForProcessRunning(specimen, this); + TargetObject session = process.getParent().getParent(); + TargetModuleContainer modules = m.find(TargetModuleContainer.class, session.getPath()); + TargetModule binMod = (TargetModule) waitOn(m.getAddedWaiter() + .wait(PathUtils.index(modules.getPath(), ((FridaLinuxSpecimen)specimen).getShortName()))); + + // NB. this heuristic assumes all function bodies are contiguous in memory + TargetSymbolNamespace symbols = m.find(TargetSymbolNamespace.class, binMod.getPath()); + // NB. ONCE is recommended resync mode for module symbols + for (Entry entry : waitOn(symbols.fetchElements()) + .entrySet()) { + symbolsByKey.put(entry.getKey(), entry.getValue().as(TargetSymbol.class)); + } + + return process; +} + + protected void runTestResume(DebuggerTestSpecimen specimen) throws Throwable { + TargetProcess process = retryForProcessRunning(specimen, this); + TargetResumable resumable = m.suitable(TargetResumable.class, process.getPath()); + AsyncState state = + new AsyncState(m.suitable(TargetExecutionStateful.class, process.getPath())); + TargetExecutionState st = waitOn(state.waitUntil(s -> s != TargetExecutionState.RUNNING)); + assertTrue(st.isAlive()); + waitOn(resumable.resume()); + retryVoid(() -> assertTrue(DebugModelConventions.isProcessAlive(process)), + List.of(AssertionError.class)); + } + + protected void runTestKill(DebuggerTestSpecimen specimen) throws Throwable { + TargetProcess process = retryForProcessRunning(specimen, this); + TargetKillable killable = m.suitable(TargetKillable.class, process.getPath()); + waitOn(killable.kill()); + } + + protected void runTestLaunchThenResume(TargetLauncher launcher) throws Throwable { + DebuggerTestSpecimen specimen = getPrintSpecimen(); + assertNull(getProcessRunning(specimen, this)); + runTestLaunch(specimen, launcher); + runTestResumeTerminates(specimen); + } + + @Test + public void testLaunchResumeKill() throws Throwable { + assumeTrue(m.hasKillableProcesses()); + m.build(); + + TargetLauncher launcher = findLauncher(); + DebuggerTestSpecimen specimen = getPrintSpecimen(); + assertNull(getProcessRunning(specimen, this)); + runTestLaunch(specimen, launcher); + runTestResume(specimen); + runTestKill(specimen); + } + + @Test + public void testScan() throws Throwable { + assumeTrue(m.hasKillableProcesses()); + m.build(); + + TargetLauncher launcher = findLauncher(); + DebuggerTestSpecimen specimen = getPrintSpecimen(); + assertNull(getProcessRunning(specimen, this)); + TargetProcess process = runTestLaunch(specimen, launcher); + + FridaModelTargetProcess fproc = (FridaModelTargetProcess) process; + ConsoleEventListener listener = new ConsoleEventListener("Found match at"); + fproc.getManager().addEventsListener(listener); + + FridaModelTargetMemoryContainerImpl memory = (FridaModelTargetMemoryContainerImpl) fproc.getCachedAttribute("Memory"); + FridaModelTargetMemoryScanImpl scan = (FridaModelTargetMemoryScanImpl) memory.getCachedAttribute("scan"); + Map map = new HashMap<>(); + Address address = symbolsByKey.get("overwrite").getValue(); + map.put("Address", address.toString()); + map.put("Size", 10L); + map.put("Pattern", "48 65 6C 6C 6F"); + map.put("Stop", true); + scan.invoke(map); + + waitForCondition(() -> { + return listener.foundMatch(); + }, "Console output timed out"); + assertTrue(listener.getMatchingOutput().contains(address.toString())); + runTestKill(specimen); + } + + @Test + public void testWatch() throws Throwable { + assumeTrue(m.hasKillableProcesses()); + m.build(); + + TargetLauncher launcher = findLauncher(); + DebuggerTestSpecimen specimen = getPrintSpecimen(); + assertNull(getProcessRunning(specimen, this)); + TargetProcess process = runTestLaunch(specimen, launcher); + + FridaModelTargetProcess fproc = (FridaModelTargetProcess) process; + ConsoleEventListener listener = new ConsoleEventListener("read"); + fproc.getManager().addEventsListener(listener); + + FridaModelTargetMemoryContainerImpl memory = (FridaModelTargetMemoryContainerImpl) fproc.getCachedAttribute("Memory"); + FridaModelTargetMemoryWatchImpl watch = (FridaModelTargetMemoryWatchImpl) memory.getCachedAttribute("watch"); + Map map = new HashMap<>(); + Address address = symbolsByKey.get("overwrite").getValue(); + map.put("Address", address.toString()); + map.put("Size", 1L); + ResourceFile installationDirectory = Application.getInstallationDirectory(); + map.put("OnAccess", installationDirectory + "/ghidra/Ghidra/Debug/Debugger-agent-frida/data/scripts/onAccess.js"); + watch.invoke(map); + runTestResume(specimen); + + waitForCondition(() -> { + return listener.foundMatch(); + }, "Console output timed out"); + assertTrue(listener.getMatchingOutput().contains(address.toString())); + runTestKill(specimen); + } + + @Test + public void testInterceptor() throws Throwable { + assumeTrue(m.hasKillableProcesses()); + m.build(); + + TargetLauncher launcher = findLauncher(); + DebuggerTestSpecimen specimen = getStackSpecimen(); + assertNull(getProcessRunning(specimen, this)); + TargetProcess process = runTestLaunch(specimen, launcher); + + FridaModelTargetProcess fproc = (FridaModelTargetProcess) process; + ConsoleEventListener listener = new ConsoleEventListener("entering"); + fproc.getManager().addEventsListener(listener); + + Map map = new HashMap<>(); + FridaModelTargetSymbol symbol = (FridaModelTargetSymbol) symbolsByKey.get("break_here"); + FridaModelTargetFunctionInterceptorImpl intercept = + (FridaModelTargetFunctionInterceptorImpl) symbol.getCachedAttribute("intercept"); + ResourceFile installationDirectory = Application.getInstallationDirectory(); + map.put("OnEnter", installationDirectory + "/ghidra/Ghidra/Debug/Debugger-agent-frida/data/scripts/onEnter.js"); + map.put("OnLeave", ""); + intercept.invoke(map); + runTestResume(specimen); + + waitForCondition(() -> { + return listener.foundMatch(); + }, "Console output timed out"); + runTestKill(specimen); + } + + @Test + public void testStalker() throws Throwable { + assumeTrue(m.hasKillableProcesses()); + m.build(); + + TargetLauncher launcher = findLauncher(); + DebuggerTestSpecimen specimen = getSpinSpecimen(); + assertNull(getProcessRunning(specimen, this)); + TargetProcess process = runTestLaunch(specimen, launcher); + + FridaModelTargetProcess fproc = (FridaModelTargetProcess) process; + ConsoleEventListener listener = new ConsoleEventListener(":1"); + fproc.getManager().addEventsListener(listener); + FridaModelTargetThreadContainerImpl threads = (FridaModelTargetThreadContainerImpl) fproc.getCachedAttribute("Threads"); + Map elements = threads.getCachedElements(); + FridaModelTargetThreadImpl thread = (FridaModelTargetThreadImpl) elements.values().iterator().next(); + + Map map = new HashMap<>(); + FridaModelTargetThreadStalkImpl stalk = + (FridaModelTargetThreadStalkImpl) thread.getCachedAttribute("stalk"); + ResourceFile installationDirectory = Application.getInstallationDirectory(); + map.put("OnCallSummary", installationDirectory + "/ghidra/Ghidra/Debug/Debugger-agent-frida/data/scripts/onCallSummary.js"); + map.put("EventCall", true); + map.put("EventRet", false); + map.put("EventExec", false); + map.put("EventBlock", false); + map.put("EventCompile", false); + map.put("OnReceive", ""); + stalk.invoke(map); + runTestResume(specimen); + Thread.sleep(1000); + + waitForCondition(() -> { + return listener.foundMatch(); + }, "Console output timed out"); + runTestKill(specimen); + } + + private class ConsoleEventListener implements FridaEventsListenerAdapter { + + private String match; + private boolean foundMatch = false; + private String matchingOutput; + + public ConsoleEventListener(String match) { + this.match = match; + } + + @Override + public void consoleOutput(String output, int mask) { + if (output.contains(match)) { + foundMatch = true; + matchingOutput = output; + } + } + + public boolean foundMatch() { + return foundMatch; + } + public String getMatchingOutput() { + return matchingOutput; + } + } + +} diff --git a/Ghidra/Debug/Debugger-agent-frida/src/test/java/agent/frida/model/AbstractModelForFridaRootAttacherTest.java b/Ghidra/Debug/Debugger-agent-frida/src/test/java/agent/frida/model/AbstractModelForFridaRootAttacherTest.java new file mode 100644 index 0000000000..f5b05cc316 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-frida/src/test/java/agent/frida/model/AbstractModelForFridaRootAttacherTest.java @@ -0,0 +1,102 @@ +/* ### + * 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.model; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import java.util.List; +import java.util.concurrent.TimeUnit; + +import org.junit.After; +import org.junit.Ignore; +import org.junit.Test; + +import agent.frida.model.iface1.FridaModelTargetKillable; +import ghidra.dbg.DebugModelConventions; +import ghidra.dbg.target.TargetEnvironment; +import ghidra.dbg.target.TargetKillable; +import ghidra.dbg.target.TargetMethod.TargetParameterMap; +import ghidra.dbg.target.TargetProcess; +import ghidra.dbg.test.AbstractDebuggerModelAttacherTest; +import ghidra.dbg.util.PathUtils; +import ghidra.util.Msg; + +public abstract class AbstractModelForFridaRootAttacherTest + extends AbstractDebuggerModelAttacherTest { + + @Override + public List getExpectedAttachableContainerPath() { + return List.of("Available"); + } + + @Override + public List getExpectedAttacherPath() { + return PathUtils.parse(""); + } + + @Override + public DebuggerTestSpecimen getAttachSpecimen() { + return FridaLinuxSpecimen.SPIN_STRIPPED; + } + + @Override + public TargetParameterMap getExpectedAttachParameters() { + return null; + } + + @Override + public void assertEnvironment(TargetEnvironment environment) { + assertTrue(environment.getArchitecture().startsWith("x64")); + assertTrue(environment.getDebugger().toLowerCase().contains("frida")); + } + + @Override + @After + public void tearDownDebuggerModelTest() throws Throwable { + /** + * NB. Model has to be closed before dummy. If dummy is suspended by a debugger, terminating + * it, even forcibly, may fail. + */ + if (m != null) { + m.close(); + } + if (dummy != null) { + if (!dummy.process.destroyForcibly().waitFor(1000, TimeUnit.MILLISECONDS)) { + Msg.error(this, "Could not terminate process " + dummy.process.pid()); + //throw new TimeoutException("Could not terminate process " + pid); + } + //dummy.close(); + } + } + + @Ignore + @Test + public void testAttachByPidThenResumeInterrupt() throws Throwable { + } + + @Override + protected void runTestKill(DebuggerTestSpecimen specimen) + throws Throwable { + TargetProcess process = retryForProcessRunning(specimen, this); + FridaModelTargetKillable killable = + (FridaModelTargetKillable) m.suitable(TargetKillable.class, process.getPath()); + waitAcc(killable); + waitOn(killable.destroy()); + retryVoid(() -> assertFalse(DebugModelConventions.isProcessAlive(process)), + List.of(AssertionError.class)); + } +} diff --git a/Ghidra/Debug/Debugger-agent-frida/src/test/java/agent/frida/model/AbstractModelForFridaRootLauncherTest.java b/Ghidra/Debug/Debugger-agent-frida/src/test/java/agent/frida/model/AbstractModelForFridaRootLauncherTest.java new file mode 100644 index 0000000000..207066aa65 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-frida/src/test/java/agent/frida/model/AbstractModelForFridaRootLauncherTest.java @@ -0,0 +1,81 @@ +/* ### + * 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.model; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +import java.util.List; +import java.util.Map; + +import ghidra.dbg.DebugModelConventions.AsyncState; +import ghidra.dbg.target.TargetDetachable; +import ghidra.dbg.target.TargetEnvironment; +import ghidra.dbg.target.TargetExecutionStateful; +import ghidra.dbg.target.TargetExecutionStateful.TargetExecutionState; +import ghidra.dbg.target.TargetMethod.ParameterDescription; +import ghidra.dbg.target.TargetMethod.TargetParameterMap; +import ghidra.dbg.target.TargetProcess; +import ghidra.dbg.target.TargetResumable; +import ghidra.dbg.test.AbstractDebuggerModelLauncherTest; +import ghidra.dbg.util.PathUtils; + +public abstract class AbstractModelForFridaRootLauncherTest + extends AbstractDebuggerModelLauncherTest { + + @Override + public List getExpectedLauncherPath() { + return PathUtils.parse(""); + } + + @Override + public DebuggerTestSpecimen getLaunchSpecimen() { + return FridaLinuxSpecimen.PRINT; + } + + @Override + public TargetParameterMap getExpectedLauncherParameters() { + return TargetParameterMap.copyOf(Map.ofEntries( + Map.entry("args", ParameterDescription.create(String.class, "args", true, "", + "Command Line", "space-separated command-line arguments")))); + } + + @Override + public void assertEnvironment(TargetEnvironment environment) { + assertEquals("x64", environment.getArchitecture()); + //assertTrue(environment.getOperatingSystem().startsWith("macos")); + assertTrue(environment.getDebugger().toLowerCase().contains("frida")); + } + + protected void runTestDetach(DebuggerTestSpecimen specimen) + throws Throwable { + TargetProcess process = retryForProcessRunning(specimen, this); + TargetDetachable detachable = m.suitable(TargetDetachable.class, process.getPath()); + waitAcc(detachable); + waitOn(detachable.detach()); + } + + protected void runTestResumeTerminates(DebuggerTestSpecimen specimen) throws Throwable { + TargetProcess process = retryForProcessRunning(specimen, this); + TargetResumable resumable = m.suitable(TargetResumable.class, process.getPath()); + AsyncState state = + new AsyncState(m.suitable(TargetExecutionStateful.class, process.getPath())); + TargetExecutionState st = waitOn(state.waitUntil(s -> s == TargetExecutionState.STOPPED)); + assertTrue(st.isAlive()); + waitOn(resumable.resume()); + } + +} diff --git a/Ghidra/Debug/Debugger-agent-frida/src/test/java/agent/frida/model/AbstractModelForFridaScenarioStackTest.java b/Ghidra/Debug/Debugger-agent-frida/src/test/java/agent/frida/model/AbstractModelForFridaScenarioStackTest.java new file mode 100644 index 0000000000..e3dfb96c8f --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-frida/src/test/java/agent/frida/model/AbstractModelForFridaScenarioStackTest.java @@ -0,0 +1,127 @@ +/* ### + * 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.model; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + +import java.util.List; +import java.util.Map.Entry; +import java.util.NavigableMap; +import java.util.TreeMap; + +import ghidra.dbg.DebugModelConventions; +import ghidra.dbg.DebugModelConventions.AsyncState; +import ghidra.dbg.target.TargetExecutionStateful; +import ghidra.dbg.target.TargetLauncher; +import ghidra.dbg.target.TargetModule; +import ghidra.dbg.target.TargetModuleContainer; +import ghidra.dbg.target.TargetObject; +import ghidra.dbg.target.TargetProcess; +import ghidra.dbg.target.TargetStack; +import ghidra.dbg.target.TargetStackFrame; +import ghidra.dbg.target.TargetSymbol; +import ghidra.dbg.target.TargetSymbolNamespace; +import ghidra.dbg.test.AbstractDebuggerModelScenarioStackTest; +import ghidra.dbg.util.PathMatcher; +import ghidra.dbg.util.PathPattern; +import ghidra.dbg.util.PathUtils; +import ghidra.program.model.address.Address; +import ghidra.util.Msg; + +public abstract class AbstractModelForFridaScenarioStackTest + extends AbstractDebuggerModelScenarioStackTest { + protected static List expectedSymbols = + List.of("_end", "_end", "_end", "_end"); + protected NavigableMap symbolsByAddress = new TreeMap<>(); + + @Override + protected FridaLinuxSpecimen getSpecimen() { + return FridaLinuxSpecimen.STACK; + } + + public void testScenario() throws Throwable { + DebuggerTestSpecimen specimen = getSpecimen(); + m.build(); + + Msg.debug(this, "Launching " + specimen); + TargetLauncher launcher = findLauncher(); + waitOn(launcher.launch(specimen.getLauncherArgs())); + Msg.debug(this, " Done launching"); + TargetProcess process = retryForProcessRunning(specimen, this); + postLaunch(process); + + assertTrue(DebugModelConventions.isProcessAlive(process)); + AsyncState state = + new AsyncState(m.suitable(TargetExecutionStateful.class, process.getPath())); + + assertTrue(state.get().isAlive()); + + TargetStack stack = findStack(process.getPath()); + PathMatcher matcher = stack.getSchema().searchFor(TargetStackFrame.class, true); + PathPattern pattern = matcher.getSingletonPattern(); + assertNotNull("Frames are not clearly indexable", pattern); + assertEquals("Frames are not clearly indexable", 1, pattern.countWildcards()); + // Sort by path should present them innermost to outermost + List frames = retry(() -> { + List result = + List.copyOf(m.findAll(TargetStackFrame.class, stack.getPath(), true).values()); + assertTrue("Fewer than 4 frames", result.size() > 4); + return result; + }, List.of(AssertionError.class)); + for (int i = 0; i < 4; i++) { + TargetStackFrame f = frames.get(i); + validateFramePC(i, f.getProgramCounter()); + } + + } + + @Override + protected void postLaunch(TargetProcess process) throws Throwable { + TargetObject session = process.getParent().getParent(); + TargetModuleContainer modules = m.find(TargetModuleContainer.class, session.getPath()); + // NB. NEVER is recommended resync mode for modules container + // It's not guaranteed to come before process is alive, though + TargetModule binMod = (TargetModule) waitOn(m.getAddedWaiter() + .wait(PathUtils.index(modules.getPath(), getSpecimen().getShortName()))); + + // NB. this heuristic assumes all function bodies are contiguous in memory + TargetSymbolNamespace symbols = m.find(TargetSymbolNamespace.class, binMod.getPath()); + // NB. ONCE is recommended resync mode for module symbols + for (Entry entry : waitOn(symbols.fetchElements()) + .entrySet()) { + symbolsByAddress.put(entry.getValue().as(TargetSymbol.class).getValue(), + entry.getKey()); + } + } + + @Override + protected TargetStack findStack(List seedPath) throws Throwable { + return m.findAny(TargetStack.class, seedPath); + } + + @Override + protected void validateFramePC(int index, Address pc) { + assertEquals(expectedSymbols.get(index), symbolsByAddress.floorEntry(pc).getValue()); + } + + @Override + protected String getBreakpointExpression() { + return null; + } + +} diff --git a/Ghidra/Debug/Debugger-agent-frida/src/test/java/agent/frida/model/AbstractModelForFridaX64RegistersTest.java b/Ghidra/Debug/Debugger-agent-frida/src/test/java/agent/frida/model/AbstractModelForFridaX64RegistersTest.java new file mode 100644 index 0000000000..d52e315892 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-frida/src/test/java/agent/frida/model/AbstractModelForFridaX64RegistersTest.java @@ -0,0 +1,169 @@ +/* ### + * 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.model; + +import static org.junit.Assert.assertArrayEquals; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; +import static org.junit.Assume.assumeNotNull; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Map.Entry; + +import org.junit.Ignore; +import org.junit.Test; + +import ghidra.dbg.target.TargetObject; +import ghidra.dbg.target.TargetRegister; +import ghidra.dbg.target.TargetRegisterBank; +import ghidra.dbg.target.TargetRegisterContainer; +import ghidra.dbg.test.AbstractDebuggerModelRegistersTest; +import ghidra.dbg.test.AbstractDebuggerModelTest; +import ghidra.dbg.test.ProvidesTargetViaLaunchSpecimen; +import ghidra.dbg.util.PathUtils; + +public abstract class AbstractModelForFridaX64RegistersTest + extends AbstractDebuggerModelRegistersTest + implements ProvidesTargetViaLaunchSpecimen { + public final Map REG_VALS = Map.ofEntries( + Map.entry("rax", arr("0123456789abcdef")), + Map.entry("rsp", arr("0123456789abcdef"))); + + @Override + public AbstractDebuggerModelTest getTest() { + return this; + } + + @Override + public boolean isRegisterBankAlsoContainer() { + return true; + } + + @Override + public List getExpectedRegisterBankPath(List threadPath) { + return PathUtils.extend(threadPath, PathUtils.parse("Registers")); + } + + @Override + public Map getRegisterWrites() { + return REG_VALS; + } + + @Override + public DebuggerTestSpecimen getLaunchSpecimen() { + return FridaLinuxSpecimen.PRINT; + } + + @Override + @Test + public void testRegistersHaveExpectedSizes() throws Throwable { + m.build(); + + TargetObject target = maybeSubstituteThread(obtainTarget()); + Map, TargetRegisterBank> banks = findRegisterBanks(target.getPath()); + for (TargetRegisterBank bank : banks.values()) { + List path = bank.getPath(); + for (Entry ent : getRegisterWrites().entrySet()) { + String regName = ent.getKey(); + Map, TargetRegister> regs = m.findAll(TargetRegister.class, + path, pred -> pred.applyIndices(regName), false); + for (TargetRegister reg : regs.values()) { + assertEquals(ent.getValue().length, (reg.getBitLength() + 7) / 8); + } + } + } + } + + @Override + @Test + public void testRegisterBankIsWhereExpected() throws Throwable { + m.build(); + + TargetObject target = maybeSubstituteThread(obtainTarget()); + List expectedRegisterBankPath = + getExpectedRegisterBankPath(target.getPath()); + assumeNotNull(expectedRegisterBankPath); + + Map, TargetRegisterBank> banks = findRegisterBanks(target.getPath()); + for (TargetRegisterBank bank : banks.values()) { + List path = bank.getPath(); + assertTrue(path.containsAll(expectedRegisterBankPath)); + } + } + + @Override + @Test + public void testReadRegisters() throws Throwable { + m.build(); + + TargetObject target = maybeSubstituteThread(obtainTarget()); + TargetRegisterContainer c = Objects.requireNonNull( + m.findWithIndex(TargetRegisterContainer.class, "0", target.getPath())); + Map, TargetRegisterBank> banks = + m.findAll(TargetRegisterBank.class, c.getPath(), true); + Map exp = getRegisterWrites(); + Map read = new HashMap<>(); + for (TargetRegisterBank bank : banks.values()) { + for (String name : exp.keySet()) { + Map, TargetRegister> regs = m.findAll(TargetRegister.class, + bank.getPath(), pred -> pred.applyIndices(name), false); + for (TargetRegister reg : regs.values()) { + byte[] bytes = waitOn(bank.readRegister(reg)); + read.put(name, bytes); + expectRegisterObjectValue(bank, name, bytes); + assertEquals(exp.get(name).length, bytes.length); + } + } + } + assertEquals("Not all registers were read, or extras were read", exp.keySet(), + read.keySet()); + } + + @Override + @Ignore + @Test + public void testWriteRegisters() throws Throwable { + m.build(); + + TargetObject target = maybeSubstituteThread(obtainTarget()); + TargetRegisterContainer c = Objects.requireNonNull( + m.findWithIndex(TargetRegisterContainer.class, "0", target.getPath())); + Map, TargetRegisterBank> banks = + m.findAll(TargetRegisterBank.class, c.getPath(), true); + Map write = getRegisterWrites(); + Map read = new HashMap<>(); + for (TargetRegisterBank bank : banks.values()) { + for (String name : write.keySet()) { + Map, TargetRegister> regs = m.findAll(TargetRegister.class, + bank.getPath(), pred -> pred.applyIndices(name), false); + for (TargetRegister reg : regs.values()) { + waitOn(bank.writeRegister(reg, write.get(name))); + + // NB. This only really tests the cache, if applicable. A scenario checks for efficacy. + byte[] bytes = waitOn(bank.readRegister(reg)); + read.put(name, bytes); + expectRegisterObjectValue(bank, name, bytes); + assertArrayEquals(write.get(name), bytes); + } + } + } + assertEquals("Not all registers were read, or extras were read", write.keySet(), + read.keySet()); + } +} diff --git a/Ghidra/Debug/Debugger-agent-frida/src/test/java/agent/frida/model/FridaLinuxSpecimen.java b/Ghidra/Debug/Debugger-agent-frida/src/test/java/agent/frida/model/FridaLinuxSpecimen.java new file mode 100644 index 0000000000..b1f2bded97 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-frida/src/test/java/agent/frida/model/FridaLinuxSpecimen.java @@ -0,0 +1,126 @@ +/* ### + * 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.model; + +import java.util.*; + +import agent.frida.model.iface1.FridaModelTargetAttachable; +import ghidra.dbg.target.*; +import ghidra.dbg.target.TargetLauncher.TargetCmdLineLauncher; +import ghidra.dbg.test.AbstractDebuggerModelTest; +import ghidra.dbg.test.AbstractDebuggerModelTest.DebuggerTestSpecimen; +import ghidra.dbg.testutil.DebuggerModelTestUtils; +import ghidra.dbg.testutil.DummyProc; +import ghidra.dbg.util.ShellUtils; + +public enum FridaLinuxSpecimen implements DebuggerTestSpecimen, DebuggerModelTestUtils { + SLEEP { + @Override + String getShortName() { + return "sleep"; + } + @Override + String getCommandLine() { + return "sleep 100000"; + } + }, + PRINT { + @Override + String getShortName() { + return "expPrint"; + } + @Override + String getCommandLine() { + return DummyProc.which("expPrint"); + } + }, + REGISTERS { + @Override + String getShortName() { + return "expRegisters"; + } + @Override + String getCommandLine() { + return DummyProc.which("expRegisters"); + } + }, + SPIN_STRIPPED { + @Override + String getShortName() { + return "expSpin"; + } + @Override + String getCommandLine() { + return DummyProc.which("expSpin"); + } + }, + STACK { + @Override + String getShortName() { + return "expStack"; + } + @Override + String getCommandLine() { + return DummyProc.which("expStack"); + } + }; + + abstract String getShortName(); + abstract String getCommandLine(); + + @Override + public DummyProc runDummy() throws Throwable { + return DummyProc.run(ShellUtils.parseArgs(getCommandLine()).toArray(String[]::new)); + } + + @Override + public Map getLauncherArgs() { + return Map.ofEntries( + Map.entry(TargetCmdLineLauncher.CMDLINE_ARGS_NAME, getCommandLine())); + } + + @Override + public List getLaunchScript() { + List script = new ArrayList<>(); + List parsed = ShellUtils.parseArgs(getCommandLine()); + if (parsed.size() > 1) { + script.add("set args " + ShellUtils.generateLine(parsed.subList(1, parsed.size()))); + } + script.add("file " + ShellUtils.generateLine(parsed.subList(0, 1))); + script.add("start"); + return script; + } + + @Override + public boolean isRunningIn(TargetProcess process, AbstractDebuggerModelTest test) + throws Throwable { + String expected = getCommandLine(); + TargetObject session = process.getParent().getParent(); + Collection modules = + test.m.findAll(TargetModule.class, session.getPath(), true).values(); + return modules.stream() + .anyMatch(m -> expected.contains(m.getShortDisplay())); + } + + @Override + public boolean isAttachable(DummyProc dummy, TargetAttachable attachable, + AbstractDebuggerModelTest test) throws Throwable { + waitOn(attachable.fetchAttributes()); + long pid = attachable.getTypedAttributeNowByName( + FridaModelTargetAttachable.PID_ATTRIBUTE_NAME, Long.class, -1L); + return pid == dummy.pid; + } +} diff --git a/Ghidra/Debug/Debugger-agent-frida/src/test/java/agent/frida/model/gadp/GadpFridaModelHost.java b/Ghidra/Debug/Debugger-agent-frida/src/test/java/agent/frida/model/gadp/GadpFridaModelHost.java new file mode 100644 index 0000000000..92b85bcf8d --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-frida/src/test/java/agent/frida/model/gadp/GadpFridaModelHost.java @@ -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.model.gadp; + +import static org.junit.Assume.assumeFalse; + +import agent.frida.gadp.FridaLocalDebuggerModelFactory; +import agent.frida.model.AbstractFridaModelHost; +import ghidra.dbg.DebuggerModelFactory; +import ghidra.util.SystemUtilities; + +class GadpFridaModelHost extends AbstractFridaModelHost { + @Override + public DebuggerModelFactory getModelFactory() { + assumeFalse("Not ready for CI", SystemUtilities.isInTestingBatchMode()); + return new FridaLocalDebuggerModelFactory(); + } +} diff --git a/Ghidra/Debug/Debugger-agent-frida/src/test/java/agent/frida/model/gadp/GadpModelForFridaFactoryTest.java b/Ghidra/Debug/Debugger-agent-frida/src/test/java/agent/frida/model/gadp/GadpModelForFridaFactoryTest.java new file mode 100644 index 0000000000..7eed21f066 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-frida/src/test/java/agent/frida/model/gadp/GadpModelForFridaFactoryTest.java @@ -0,0 +1,25 @@ +/* ### + * 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.model.gadp; + +import agent.frida.model.AbstractModelForFridaFactoryTest; + +public class GadpModelForFridaFactoryTest extends AbstractModelForFridaFactoryTest { + @Override + public ModelHost modelHost() throws Throwable { + return new GadpFridaModelHost(); + } +} diff --git a/Ghidra/Debug/Debugger-agent-frida/src/test/java/agent/frida/model/gadp/GadpModelForFridaInterpreterTest.java b/Ghidra/Debug/Debugger-agent-frida/src/test/java/agent/frida/model/gadp/GadpModelForFridaInterpreterTest.java new file mode 100644 index 0000000000..2a949604e6 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-frida/src/test/java/agent/frida/model/gadp/GadpModelForFridaInterpreterTest.java @@ -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.model.gadp; + +import org.junit.Ignore; +import org.junit.Test; + +import agent.frida.model.AbstractModelForFridaInterpreterTest; +import ghidra.dbg.test.ProvidesTargetViaLaunchSpecimen; + +public class GadpModelForFridaInterpreterTest extends AbstractModelForFridaInterpreterTest + implements ProvidesTargetViaLaunchSpecimen { + @Override + public ModelHost modelHost() throws Throwable { + return new GadpFridaModelHost(); + } + + // Not sure what the behavior for these two should be... + // "file target" will change the process and this isn't handled + // also getLaunchScript in MacOSSpecimen is currently wrong + @Override + @Ignore + @Test + public void testLaunchViaInterpreterShowsInProcessContainer() throws Throwable { + super.testLaunchViaInterpreterShowsInProcessContainer(); + } + + @Override + @Ignore + @Test + public void testAttachViaInterpreterShowsInProcessContainer() throws Throwable { + super.testAttachViaInterpreterShowsInProcessContainer(); + } + + // "quit" does not have the desired behavior + @Override + @Ignore + @Test + public void testExecuteQuit() throws Throwable { + super.testExecuteQuit(); + } + + @Override + @Ignore + @Test + public void testInterpreterIsWhereExpected() throws Throwable { + super.testInterpreterIsWhereExpected(); + } + +} + diff --git a/Ghidra/Debug/Debugger-agent-frida/src/test/java/agent/frida/model/gadp/GadpModelForFridaRootLauncherTest.java b/Ghidra/Debug/Debugger-agent-frida/src/test/java/agent/frida/model/gadp/GadpModelForFridaRootLauncherTest.java new file mode 100644 index 0000000000..cf080d57b0 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-frida/src/test/java/agent/frida/model/gadp/GadpModelForFridaRootLauncherTest.java @@ -0,0 +1,25 @@ +/* ### + * 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.model.gadp; + +import agent.frida.model.AbstractModelForFridaRootLauncherTest; + +public class GadpModelForFridaRootLauncherTest extends AbstractModelForFridaRootLauncherTest { + @Override + public ModelHost modelHost() throws Throwable { + return new GadpFridaModelHost(); + } +} diff --git a/Ghidra/Debug/Debugger-agent-frida/src/test/java/agent/frida/model/gadp/GadpModelForFridaScenarioStackTest.java b/Ghidra/Debug/Debugger-agent-frida/src/test/java/agent/frida/model/gadp/GadpModelForFridaScenarioStackTest.java new file mode 100644 index 0000000000..714bd70fc6 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-frida/src/test/java/agent/frida/model/gadp/GadpModelForFridaScenarioStackTest.java @@ -0,0 +1,25 @@ +/* ### + * 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.model.gadp; + +import agent.frida.model.AbstractModelForFridaScenarioStackTest; + +public class GadpModelForFridaScenarioStackTest extends AbstractModelForFridaScenarioStackTest { + @Override + public ModelHost modelHost() throws Throwable { + return new GadpFridaModelHost(); + } +} diff --git a/Ghidra/Debug/Debugger-agent-frida/src/test/java/agent/frida/model/gadp/GadpModelForFridaX64RegistersTest.java b/Ghidra/Debug/Debugger-agent-frida/src/test/java/agent/frida/model/gadp/GadpModelForFridaX64RegistersTest.java new file mode 100644 index 0000000000..af03676535 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-frida/src/test/java/agent/frida/model/gadp/GadpModelForFridaX64RegistersTest.java @@ -0,0 +1,27 @@ +/* ### + * 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.model.gadp; + +import agent.frida.model.AbstractModelForFridaX64RegistersTest; + +public class GadpModelForFridaX64RegistersTest extends AbstractModelForFridaX64RegistersTest { + + @Override + public ModelHost modelHost() throws Throwable { + return new GadpFridaModelHost(); + } + +} diff --git a/Ghidra/Debug/Debugger-agent-frida/src/test/java/agent/frida/model/invm/InVmFridaModelHost.java b/Ghidra/Debug/Debugger-agent-frida/src/test/java/agent/frida/model/invm/InVmFridaModelHost.java new file mode 100644 index 0000000000..03ea2c3a6e --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-frida/src/test/java/agent/frida/model/invm/InVmFridaModelHost.java @@ -0,0 +1,27 @@ +/* ### + * 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.model.invm; + +import agent.frida.FridaInJvmDebuggerModelFactory; +import agent.frida.model.AbstractFridaModelHost; +import ghidra.dbg.DebuggerModelFactory; + +public class InVmFridaModelHost extends AbstractFridaModelHost { + @Override + public DebuggerModelFactory getModelFactory() { + return new FridaInJvmDebuggerModelFactory(); + } +} diff --git a/Ghidra/Debug/Debugger-agent-frida/src/test/java/agent/frida/model/invm/InVmModelForFridaFactoryTest.java b/Ghidra/Debug/Debugger-agent-frida/src/test/java/agent/frida/model/invm/InVmModelForFridaFactoryTest.java new file mode 100644 index 0000000000..5c4c2048d6 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-frida/src/test/java/agent/frida/model/invm/InVmModelForFridaFactoryTest.java @@ -0,0 +1,25 @@ +/* ### + * 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.model.invm; + +import agent.frida.model.AbstractModelForFridaFactoryTest; + +public class InVmModelForFridaFactoryTest extends AbstractModelForFridaFactoryTest { + @Override + public ModelHost modelHost() throws Throwable { + return new InVmFridaModelHost(); + } +} diff --git a/Ghidra/Debug/Debugger-agent-frida/src/test/java/agent/frida/model/invm/InVmModelForFridaInterpreterTest.java b/Ghidra/Debug/Debugger-agent-frida/src/test/java/agent/frida/model/invm/InVmModelForFridaInterpreterTest.java new file mode 100644 index 0000000000..17b36f4627 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-frida/src/test/java/agent/frida/model/invm/InVmModelForFridaInterpreterTest.java @@ -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.model.invm; + +import org.junit.Ignore; +import org.junit.Test; + +import agent.frida.model.AbstractModelForFridaInterpreterTest; +import ghidra.dbg.test.ProvidesTargetViaLaunchSpecimen; + +public class InVmModelForFridaInterpreterTest extends AbstractModelForFridaInterpreterTest + implements ProvidesTargetViaLaunchSpecimen { + @Override + public ModelHost modelHost() throws Throwable { + return new InVmFridaModelHost(); + } + + // Not sure what the behavior for these two should be... + // "file target" will change the process and this isn't handled + // also getLaunchScript in MacOSSpecimen is currently wrong + @Override + @Ignore + @Test + public void testLaunchViaInterpreterShowsInProcessContainer() throws Throwable { + super.testLaunchViaInterpreterShowsInProcessContainer(); + } + + @Override + @Ignore + @Test + public void testAttachViaInterpreterShowsInProcessContainer() throws Throwable { + super.testAttachViaInterpreterShowsInProcessContainer(); + } + + // "quit" does not have the desired behavior + @Override + @Ignore + @Test + public void testExecuteQuit() throws Throwable { + super.testExecuteQuit(); + } + + @Override + @Ignore + @Test + public void testInterpreterIsWhereExpected() throws Throwable { + super.testInterpreterIsWhereExpected(); + } + +} + diff --git a/Ghidra/Debug/Debugger-agent-frida/src/test/java/agent/frida/model/invm/InVmModelForFridaMethodsTest.java b/Ghidra/Debug/Debugger-agent-frida/src/test/java/agent/frida/model/invm/InVmModelForFridaMethodsTest.java new file mode 100644 index 0000000000..760d548974 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-frida/src/test/java/agent/frida/model/invm/InVmModelForFridaMethodsTest.java @@ -0,0 +1,25 @@ +/* ### + * 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.model.invm; + +import agent.frida.model.AbstractModelForFridaMethodsTest; + +public class InVmModelForFridaMethodsTest extends AbstractModelForFridaMethodsTest { + @Override + public ModelHost modelHost() throws Throwable { + return new InVmFridaModelHost(); + } +} diff --git a/Ghidra/Debug/Debugger-agent-frida/src/test/java/agent/frida/model/invm/InVmModelForFridaRootAttacherTest.java b/Ghidra/Debug/Debugger-agent-frida/src/test/java/agent/frida/model/invm/InVmModelForFridaRootAttacherTest.java new file mode 100644 index 0000000000..8859c48c07 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-frida/src/test/java/agent/frida/model/invm/InVmModelForFridaRootAttacherTest.java @@ -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.model.invm; + +import org.junit.Ignore; +import org.junit.Test; + +import agent.frida.model.AbstractModelForFridaRootAttacherTest; + +public class InVmModelForFridaRootAttacherTest extends AbstractModelForFridaRootAttacherTest { + @Override + public ModelHost modelHost() throws Throwable { + return new InVmFridaModelHost(); + } + + // NB: These tests need debugger rights, which means either: + // (1) on macos, codesigning the executables + // (2) on linux, "sudo su; echo 0 > /proc/sys/kernel/yama/ptrace_scope" + + @Override + @Ignore // test requires ability to attach by object & frida version requires pid + @Test + public void testAttachByObjBogusThrowsException() throws Throwable { + super.testAttachByObjBogusThrowsException(); + } + +} diff --git a/Ghidra/Debug/Debugger-agent-frida/src/test/java/agent/frida/model/invm/InVmModelForFridaRootLauncherTest.java b/Ghidra/Debug/Debugger-agent-frida/src/test/java/agent/frida/model/invm/InVmModelForFridaRootLauncherTest.java new file mode 100644 index 0000000000..1e89fe2b39 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-frida/src/test/java/agent/frida/model/invm/InVmModelForFridaRootLauncherTest.java @@ -0,0 +1,25 @@ +/* ### + * 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.model.invm; + +import agent.frida.model.AbstractModelForFridaRootLauncherTest; + +public class InVmModelForFridaRootLauncherTest extends AbstractModelForFridaRootLauncherTest { + @Override + public ModelHost modelHost() throws Throwable { + return new InVmFridaModelHost(); + } +} diff --git a/Ghidra/Debug/Debugger-agent-frida/src/test/java/agent/frida/model/invm/InVmModelForFridaScenarioStackTest.java b/Ghidra/Debug/Debugger-agent-frida/src/test/java/agent/frida/model/invm/InVmModelForFridaScenarioStackTest.java new file mode 100644 index 0000000000..1b24b51b67 --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-frida/src/test/java/agent/frida/model/invm/InVmModelForFridaScenarioStackTest.java @@ -0,0 +1,25 @@ +/* ### + * 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.model.invm; + +import agent.frida.model.AbstractModelForFridaScenarioStackTest; + +public class InVmModelForFridaScenarioStackTest extends AbstractModelForFridaScenarioStackTest { + @Override + public ModelHost modelHost() throws Throwable { + return new InVmFridaModelHost(); + } +} diff --git a/Ghidra/Debug/Debugger-agent-frida/src/test/java/agent/frida/model/invm/InVmModelForFridaX64RegistersTest.java b/Ghidra/Debug/Debugger-agent-frida/src/test/java/agent/frida/model/invm/InVmModelForFridaX64RegistersTest.java new file mode 100644 index 0000000000..b0534cd62f --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-frida/src/test/java/agent/frida/model/invm/InVmModelForFridaX64RegistersTest.java @@ -0,0 +1,27 @@ +/* ### + * 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.model.invm; + +import agent.frida.model.AbstractModelForFridaX64RegistersTest; + +public class InVmModelForFridaX64RegistersTest extends AbstractModelForFridaX64RegistersTest { + + @Override + public ModelHost modelHost() throws Throwable { + return new InVmFridaModelHost(); + } + +} diff --git a/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/model/impl/GdbModelTargetThread.java b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/model/impl/GdbModelTargetThread.java index 57d9623f3d..5237f1c1df 100644 --- a/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/model/impl/GdbModelTargetThread.java +++ b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/model/impl/GdbModelTargetThread.java @@ -109,6 +109,7 @@ public class GdbModelTargetThread this.info = res; changeAttributes(List.of(), Map.of( // SHORT_DISPLAY_ATTRIBUTE_NAME, shortDisplay = computeShortDisplay(), // + TID_ATTRIBUTE_NAME, info.getTid(), // DISPLAY_ATTRIBUTE_NAME, display = computeDisplay() // ), "Initialized"); }); diff --git a/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/model/impl/GdbModelTargetThreadContainer.java b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/model/impl/GdbModelTargetThreadContainer.java index a3eabf42ba..a39e0be160 100644 --- a/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/model/impl/GdbModelTargetThreadContainer.java +++ b/Ghidra/Debug/Debugger-agent-gdb/src/main/java/agent/gdb/model/impl/GdbModelTargetThreadContainer.java @@ -120,7 +120,9 @@ public class GdbModelTargetThreadContainer protected void invalidateRegisterCaches() { for (GdbThread thread : inferior.getKnownThreads().values()) { GdbModelTargetThread targetThread = (GdbModelTargetThread) impl.getModelObject(thread); - targetThread.invalidateRegisterCaches(); + if (targetThread != null) { + targetThread.invalidateRegisterCaches(); + } } } @@ -147,7 +149,9 @@ public class GdbModelTargetThreadContainer for (GdbThread thread : inferior.getKnownThreads().values()) { GdbModelTargetThread targetThread = (GdbModelTargetThread) impl.getModelObject(thread); - fence.include(targetThread.stateChanged(sco)); + if (targetThread != null) { + fence.include(targetThread.stateChanged(sco)); + } } return fence.ready(); } diff --git a/Ghidra/Debug/Debugger-agent-lldb/src/main/java/agent/lldb/model/impl/LldbModelTargetThreadImpl.java b/Ghidra/Debug/Debugger-agent-lldb/src/main/java/agent/lldb/model/impl/LldbModelTargetThreadImpl.java index 267b442438..e379f81b23 100644 --- a/Ghidra/Debug/Debugger-agent-lldb/src/main/java/agent/lldb/model/impl/LldbModelTargetThreadImpl.java +++ b/Ghidra/Debug/Debugger-agent-lldb/src/main/java/agent/lldb/model/impl/LldbModelTargetThreadImpl.java @@ -88,6 +88,7 @@ public class LldbModelTargetThreadImpl extends LldbModelTargetObjectImpl ACCESSIBLE_ATTRIBUTE_NAME, accessible = false, // DISPLAY_ATTRIBUTE_NAME, getDisplay(), // STATE_ATTRIBUTE_NAME, TargetExecutionState.ALIVE, // + TID_ATTRIBUTE_NAME, thread.GetThreadID().intValue(), // SUPPORTED_STEP_KINDS_ATTRIBUTE_NAME, SUPPORTED_KINDS // ), "Initialized"); diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/objects/DebuggerObjectsProvider.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/objects/DebuggerObjectsProvider.java index d97732bfb3..9124d31f1a 100644 --- a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/objects/DebuggerObjectsProvider.java +++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/objects/DebuggerObjectsProvider.java @@ -18,15 +18,24 @@ package ghidra.app.plugin.core.debug.gui.objects; import java.awt.BorderLayout; import java.awt.Color; import java.awt.event.MouseEvent; +import java.io.PrintWriter; import java.lang.invoke.MethodHandles; -import java.util.*; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; import java.util.Map.Entry; +import java.util.Set; import java.util.concurrent.CompletableFuture; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicReference; import java.util.function.Function; -import javax.swing.*; +import javax.swing.Icon; +import javax.swing.JComponent; +import javax.swing.JPanel; import javax.swing.tree.TreePath; import org.apache.commons.collections4.map.LinkedMap; @@ -34,44 +43,131 @@ import org.apache.commons.lang3.StringUtils; import docking.ActionContext; import docking.WindowPosition; -import docking.action.*; +import docking.action.DockingAction; +import docking.action.DockingActionIf; +import docking.action.MenuData; +import docking.action.ToggleDockingAction; import docking.action.builder.ActionBuilder; import docking.action.builder.ToggleActionBuilder; +import docking.widgets.OptionDialog; import docking.widgets.table.DefaultEnumeratedColumnTableModel; import docking.widgets.tree.GTree; +import generic.jar.ResourceFile; import ghidra.app.plugin.core.debug.DebuggerCoordinates; import ghidra.app.plugin.core.debug.DebuggerPluginPackage; import ghidra.app.plugin.core.debug.gui.DebuggerResources; -import ghidra.app.plugin.core.debug.gui.DebuggerResources.*; -import ghidra.app.plugin.core.debug.gui.objects.actions.*; -import ghidra.app.plugin.core.debug.gui.objects.components.*; -import ghidra.app.services.*; -import ghidra.async.*; -import ghidra.dbg.*; +import ghidra.app.plugin.core.debug.gui.DebuggerResources.AbstractAttachAction; +import ghidra.app.plugin.core.debug.gui.DebuggerResources.AbstractConsoleAction; +import ghidra.app.plugin.core.debug.gui.DebuggerResources.AbstractDetachAction; +import ghidra.app.plugin.core.debug.gui.DebuggerResources.AbstractInterruptAction; +import ghidra.app.plugin.core.debug.gui.DebuggerResources.AbstractKillAction; +import ghidra.app.plugin.core.debug.gui.DebuggerResources.AbstractLaunchAction; +import ghidra.app.plugin.core.debug.gui.DebuggerResources.AbstractQuickLaunchAction; +import ghidra.app.plugin.core.debug.gui.DebuggerResources.AbstractRecordAction; +import ghidra.app.plugin.core.debug.gui.DebuggerResources.AbstractRefreshAction; +import ghidra.app.plugin.core.debug.gui.DebuggerResources.AbstractResumeAction; +import ghidra.app.plugin.core.debug.gui.DebuggerResources.AbstractSetBreakpointAction; +import ghidra.app.plugin.core.debug.gui.DebuggerResources.AbstractStepFinishAction; +import ghidra.app.plugin.core.debug.gui.DebuggerResources.AbstractStepIntoAction; +import ghidra.app.plugin.core.debug.gui.DebuggerResources.AbstractStepLastAction; +import ghidra.app.plugin.core.debug.gui.DebuggerResources.AbstractStepOverAction; +import ghidra.app.plugin.core.debug.gui.DebuggerResources.AbstractToggleAction; +import ghidra.app.plugin.core.debug.gui.objects.actions.DisplayAsGraphAction; +import ghidra.app.plugin.core.debug.gui.objects.actions.DisplayAsTableAction; +import ghidra.app.plugin.core.debug.gui.objects.actions.DisplayAsTreeAction; +import ghidra.app.plugin.core.debug.gui.objects.actions.DisplayAsXMLAction; +import ghidra.app.plugin.core.debug.gui.objects.actions.DisplayFilteredGraphAction; +import ghidra.app.plugin.core.debug.gui.objects.actions.DisplayFilteredTableAction; +import ghidra.app.plugin.core.debug.gui.objects.actions.DisplayFilteredTreeAction; +import ghidra.app.plugin.core.debug.gui.objects.actions.DisplayFilteredXMLAction; +import ghidra.app.plugin.core.debug.gui.objects.actions.DisplayMethodsAction; +import ghidra.app.plugin.core.debug.gui.objects.actions.ExportAsFactsAction; +import ghidra.app.plugin.core.debug.gui.objects.actions.ExportAsXMLAction; +import ghidra.app.plugin.core.debug.gui.objects.actions.ImportFromFactsAction; +import ghidra.app.plugin.core.debug.gui.objects.actions.ImportFromXMLAction; +import ghidra.app.plugin.core.debug.gui.objects.actions.OpenWinDbgTraceAction; +import ghidra.app.plugin.core.debug.gui.objects.components.DebuggerAttachDialog; +import ghidra.app.plugin.core.debug.gui.objects.components.DebuggerBreakpointDialog; +import ghidra.app.plugin.core.debug.gui.objects.components.DebuggerMethodInvocationDialog; +import ghidra.app.plugin.core.debug.gui.objects.components.DummyTargetObject; +import ghidra.app.plugin.core.debug.gui.objects.components.ObjectAttributeColumn; +import ghidra.app.plugin.core.debug.gui.objects.components.ObjectAttributeRow; +import ghidra.app.plugin.core.debug.gui.objects.components.ObjectElementColumn; +import ghidra.app.plugin.core.debug.gui.objects.components.ObjectElementRow; +import ghidra.app.plugin.core.debug.gui.objects.components.ObjectEnumeratedColumnTableModel; +import ghidra.app.plugin.core.debug.gui.objects.components.ObjectNode; +import ghidra.app.plugin.core.debug.gui.objects.components.ObjectPane; +import ghidra.app.plugin.core.debug.gui.objects.components.ObjectTable; +import ghidra.app.plugin.core.debug.gui.objects.components.ObjectTree; +import ghidra.app.script.GhidraScript; +import ghidra.app.script.GhidraScriptProvider; +import ghidra.app.script.GhidraScriptUtil; +import ghidra.app.script.GhidraState; +import ghidra.app.services.ConsoleService; +import ghidra.app.services.DebuggerListingService; +import ghidra.app.services.DebuggerModelService; +import ghidra.app.services.DebuggerStaticMappingService; +import ghidra.app.services.DebuggerTraceManagerService; +import ghidra.app.services.GraphDisplayBroker; +import ghidra.app.services.TraceRecorder; +import ghidra.async.AsyncFence; +import ghidra.async.AsyncUtils; +import ghidra.async.TypeSpec; +import ghidra.dbg.AnnotatedDebuggerAttributeListener; +import ghidra.dbg.DebugModelConventions; +import ghidra.dbg.DebuggerModelListener; +import ghidra.dbg.DebuggerObjectModel; import ghidra.dbg.error.DebuggerMemoryAccessException; -import ghidra.dbg.target.*; +import ghidra.dbg.target.TargetAccessConditioned; +import ghidra.dbg.target.TargetAttachable; +import ghidra.dbg.target.TargetAttacher; +import ghidra.dbg.target.TargetBreakpointSpecContainer; +import ghidra.dbg.target.TargetConfigurable; import ghidra.dbg.target.TargetConsole.Channel; +import ghidra.dbg.target.TargetDetachable; +import ghidra.dbg.target.TargetExecutionStateful; import ghidra.dbg.target.TargetExecutionStateful.TargetExecutionState; +import ghidra.dbg.target.TargetFocusScope; +import ghidra.dbg.target.TargetInterpreter; +import ghidra.dbg.target.TargetInterruptible; +import ghidra.dbg.target.TargetKillable; +import ghidra.dbg.target.TargetLauncher; import ghidra.dbg.target.TargetLauncher.TargetCmdLineLauncher; +import ghidra.dbg.target.TargetMethod; import ghidra.dbg.target.TargetMethod.ParameterDescription; +import ghidra.dbg.target.TargetObject; +import ghidra.dbg.target.TargetProcess; +import ghidra.dbg.target.TargetResumable; +import ghidra.dbg.target.TargetSteppable; import ghidra.dbg.target.TargetSteppable.TargetStepKind; +import ghidra.dbg.target.TargetTogglable; import ghidra.dbg.util.DebuggerCallbackReorderer; import ghidra.dbg.util.PathUtils; +import ghidra.framework.model.Project; import ghidra.framework.options.AutoOptions; import ghidra.framework.options.SaveState; -import ghidra.framework.options.annotation.*; -import ghidra.framework.plugintool.*; +import ghidra.framework.options.annotation.AutoOptionConsumed; +import ghidra.framework.options.annotation.AutoOptionDefined; +import ghidra.framework.options.annotation.HelpInfo; +import ghidra.framework.plugintool.AutoConfigState; +import ghidra.framework.plugintool.AutoService; +import ghidra.framework.plugintool.ComponentProviderAdapter; +import ghidra.framework.plugintool.PluginTool; import ghidra.framework.plugintool.annotation.AutoConfigStateField; import ghidra.framework.plugintool.annotation.AutoServiceConsumed; import ghidra.program.model.address.Address; import ghidra.program.model.address.AddressRange; import ghidra.program.model.listing.Program; +import ghidra.program.util.ProgramLocation; +import ghidra.program.util.ProgramSelection; import ghidra.trace.model.Trace; import ghidra.trace.model.thread.TraceThread; import ghidra.util.HelpLocation; +import ghidra.util.Msg; import ghidra.util.Swing; import ghidra.util.datastruct.PrivatelyQueuedListener; import ghidra.util.table.GhidraTable; +import ghidra.util.task.TaskMonitor; import resources.ResourceManager; public class DebuggerObjectsProvider extends ComponentProviderAdapter @@ -253,6 +349,9 @@ public class DebuggerObjectsProvider extends ComponentProviderAdapter private boolean ignoreState = false; Set configurables = new HashSet<>(); + private String lastMethod = ""; + Map scripts = new HashMap<>(); + Map scriptNames = new HashMap<>(); public DebuggerObjectsProvider(final DebuggerObjectsPlugin plugin, DebuggerObjectModel model, ObjectContainer container, boolean asTree) throws Exception { @@ -859,6 +958,22 @@ public class DebuggerObjectsProvider extends ComponentProviderAdapter return result != null; } + public boolean hasInstance(ActionContext context, Class clazz) { + TargetObject object = this.getObjectFromContext(context); + if (object == null) { + return false; + } + if (isLocalOnly()) { + return clazz.isInstance(object); + } + for (Object attr : object.getCachedAttributes().values()) { + if (clazz.isInstance(attr)) { + return true; + } + } + return false; + } + public TargetObject getAncestor(ActionContext context, Class clazz) { TargetObject object = this.getObjectFromContext(context); TargetObject ref = object; @@ -1010,6 +1125,22 @@ public class DebuggerObjectsProvider extends ComponentProviderAdapter .buildAndInstallLocal(this); groupTargetIndex++; + + new ActionBuilder("Method", plugin.getName()) + .keyBinding("M") + .menuPath("Exec &Method") + .menuGroup(DebuggerResources.GROUP_TARGET, "T" + groupTargetIndex) + .menuIcon(AbstractDetachAction.ICON) + .popupMenuPath("Exec &Method") + .popupMenuGroup(DebuggerResources.GROUP_TARGET, "T" + groupTargetIndex) + .popupMenuIcon(AbstractDetachAction.ICON) + .helpLocation(AbstractAttachAction.help(plugin)) + .enabledWhen(ctx -> hasInstance(ctx, TargetMethod.class)) + .onAction(ctx -> performMethod(ctx)) + .enabled(true) + .buildAndInstallLocal(this); + + groupTargetIndex++; /* new ActionBuilder("AttachAction", plugin.getName()) @@ -1054,8 +1185,8 @@ public class DebuggerObjectsProvider extends ComponentProviderAdapter .popupMenuGroup(DebuggerResources.GROUP_TARGET, "T" + groupTargetIndex) .popupMenuIcon(AbstractKillAction.ICON) .helpLocation(AbstractKillAction.help(plugin)) - .enabledWhen(ctx -> isInstance(ctx, TargetKillable.class) && isStopped(ctx)) - .popupWhen(ctx -> isInstance(ctx, TargetKillable.class) && isStopped(ctx)) + .enabledWhen(ctx -> isInstance(ctx, TargetKillable.class) && (isStopped(ctx) || !isAccessConditioned(ctx))) + .popupWhen(ctx -> isInstance(ctx, TargetKillable.class) && (isStopped(ctx) || !isAccessConditioned(ctx))) .onAction(ctx -> performKill(ctx)) .enabled(false) .buildAndInstallLocal(this); @@ -1431,6 +1562,89 @@ public class DebuggerObjectsProvider extends ComponentProviderAdapter }).exceptionally(DebuggerResources.showError(getComponent(), "Couldn't re-attach")); } + public void performMethod(ActionContext context) { + TargetObject obj = getObjectFromContext(context); + List list = new ArrayList<>(); + Map attributes = obj.getCachedAttributes(); + for (Entry entry : attributes.entrySet()) { + if (entry.getValue() instanceof TargetMethod) { + list.add(entry.getKey()); + } + } + String choice = OptionDialog.showInputChoiceDialog(getComponent(), "Methods", "Methods", list.toArray(new String [] {}), lastMethod, OptionDialog.QUESTION_MESSAGE); + if (choice != null) { + TargetMethod method = (TargetMethod) attributes.get(choice); + Map args = launchDialog.promptArguments(method.getParameters()); + if (args != null) { + String script = (String) args.get("Script"); + if (script != null && !script.isEmpty()) { + mapScript(args); + } + method.invoke(args); + if (!choice.equals("unload")) { + lastMethod = choice; + } + } + } + } + + private void mapScript(Map args) { + String name = (String) args.get("Name"); + String scriptName = (String) args.get("Script"); + if (name.isEmpty() || scriptName.isEmpty()) { + return; + } + + ResourceFile sourceFile = GhidraScriptUtil.findScriptByName(scriptName); + if (sourceFile == null) { + Msg.error(this, "Couldn't find script"); + return; + } + GhidraScriptProvider provider = GhidraScriptUtil.getProvider(sourceFile); + if (provider == null) { + Msg.error(this, "Couldn't find script provider"); + return; + } + + PrintWriter writer = consoleService.getStdOut(); + GhidraScript script; + try { + script = provider.getScriptInstance(sourceFile, writer); + } catch (ClassNotFoundException | InstantiationException | IllegalAccessException e) { + Msg.error(this, e.getMessage()); + return; + } + scripts.put(name, script); + scriptNames.put(name, scriptName); + } + + private void fireScript(String key, String [] args) { + GhidraScript script = scripts.get(key); + String scriptName = scriptNames.get(key); + if (script == null || scriptName == null) { + return; + } + + PluginTool tool = plugin.getTool(); + Project project = tool.getProject(); + + ProgramLocation currentLocation = listingService.getCurrentLocation(); + ProgramSelection currentSelection = listingService.getCurrentSelection(); + + GhidraState state = new GhidraState(tool, project, currentProgram, + currentLocation, currentSelection, null); + + PrintWriter writer = consoleService.getStdOut(); + TaskMonitor monitor = TaskMonitor.DUMMY; + script.set(state, monitor, writer); + + try { + script.runScript(scriptName, args); + } catch (Exception e) { + e.printStackTrace(); + } + } + public void startRecording(TargetProcess targetObject, boolean prompt) { TraceRecorder rec = modelService.getRecorder(targetObject); if (rec != null) { @@ -1556,6 +1770,14 @@ public class DebuggerObjectsProvider extends ComponentProviderAdapter }, "Couldn't show interpreter"); } + public boolean isAccessConditioned(ActionContext context) { + TargetObject object = this.getObjectFromContext(context); + if (object == null) { + return false; + } + return object instanceof TargetAccessConditioned; + } + public boolean isStopped(ActionContext context) { TargetObject object = this.getObjectFromContext(context); if (object == null) { @@ -1606,9 +1828,21 @@ public class DebuggerObjectsProvider extends ComponentProviderAdapter } @Override - public void consoleOutput(TargetObject console, Channel channel, String out) { - //getPlugin().showConsole((TargetInterpreter) console); - System.err.println("consoleOutput: " + out); + public void consoleOutput(TargetObject console, Channel channel, byte [] bytes) { + String ret = new String(bytes); + if (ret.contains(TargetMethod.REDIRECT)) { + String[] split = ret.split(TargetMethod.REDIRECT); + String key = split[0]; + String val = split[1]; + GhidraScript script = scripts.get(key); + if (script != null) { + String [] args = new String[1]; + args[0] = val; + fireScript(key, args); + return; + } + } + System.err.println("consoleOutput: " + new String(ret)); } @AttributeCallback(TargetObject.DISPLAY_ATTRIBUTE_NAME) diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/platform/arm/FridaArmDebuggerMappingOpinion.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/platform/arm/FridaArmDebuggerMappingOpinion.java new file mode 100644 index 0000000000..7a921ddad8 --- /dev/null +++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/platform/arm/FridaArmDebuggerMappingOpinion.java @@ -0,0 +1,74 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.app.plugin.core.debug.platform.arm; + +import java.util.Set; + +import ghidra.app.plugin.core.debug.mapping.*; +import ghidra.dbg.target.*; +import ghidra.program.model.lang.*; +import ghidra.util.Msg; + +/** + * TODO: How does Frida name its target architectures? If same as GNU, use that. If not, maybe we + * should add those external names to .ldefs? It'd be nice to have an .ldefs-based opinion that this + * can be refactored onto. + */ +public class FridaArmDebuggerMappingOpinion implements DebuggerMappingOpinion { + protected static final LanguageID LANG_ID_AARCH64 = new LanguageID("AARCH64:LE:64:v8A"); + protected static final CompilerSpecID COMP_ID_DEFAULT = new CompilerSpecID("default"); + + protected static class FridaI386X86_64RegisterMapper extends DefaultDebuggerRegisterMapper { + public FridaI386X86_64RegisterMapper(CompilerSpec cSpec, + TargetRegisterContainer targetRegContainer) { + super(cSpec, targetRegContainer, false); + } + + @Override + protected String normalizeName(String name) { + name = super.normalizeName(name); + if ("rflags".equals(name)) { + return "eflags"; + } + return name; + } + } + + protected static class FridaAarch64MacosOffer extends DefaultDebuggerMappingOffer { + public FridaAarch64MacosOffer(TargetProcess process) { + super(process, 50, "AARCH64/Frida on macos", LANG_ID_AARCH64,COMP_ID_DEFAULT, + Set.of("cpsr")); + } + } + + @Override + public Set offersForEnv(TargetEnvironment env, TargetProcess process, + boolean includesOverrides) { + if (!env.getDebugger().toLowerCase().contains("frida")) { + return Set.of(); + } + String arch = env.getArchitecture(); + boolean is64Bit = arch.contains("AARCH64") || arch.contains("arm64") || arch.contains("arm"); + String os = env.getOperatingSystem(); + if (os.contains("macos")) { + if (is64Bit) { + Msg.info(this, "Using os=" + os + " arch=" + arch); + return Set.of(new FridaAarch64MacosOffer(process)); + } + } + return Set.of(); + } +} diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/platform/frida/FridaDebuggerProgramLaunchOpinion.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/platform/frida/FridaDebuggerProgramLaunchOpinion.java new file mode 100644 index 0000000000..2249a52aad --- /dev/null +++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/platform/frida/FridaDebuggerProgramLaunchOpinion.java @@ -0,0 +1,118 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.app.plugin.core.debug.platform.frida; + +import java.util.*; + +import ghidra.app.plugin.core.debug.service.model.launch.*; +import ghidra.app.services.DebuggerModelService; +import ghidra.dbg.DebuggerModelFactory; +import ghidra.dbg.target.TargetLauncher.TargetCmdLineLauncher; +import ghidra.dbg.target.TargetMethod.ParameterDescription; +import ghidra.dbg.util.PathUtils; +import ghidra.framework.plugintool.PluginTool; +import ghidra.program.model.listing.Program; + +public class FridaDebuggerProgramLaunchOpinion implements DebuggerProgramLaunchOpinion { + protected static abstract class AbstractFridaDebuggerProgramLaunchOffer + extends AbstractDebuggerProgramLaunchOffer { + + public AbstractFridaDebuggerProgramLaunchOffer(Program program, PluginTool tool, + DebuggerModelFactory factory) { + super(program, tool, factory); + } + + @Override + public String getMenuParentTitle() { + return "Debug " + program.getName(); + } + + @Override + protected List getLauncherPath() { + return PathUtils.parse(""); + } + + @Override + protected Map generateDefaultLauncherArgs( + Map> params) { + return Map.of(TargetCmdLineLauncher.CMDLINE_ARGS_NAME, program.getExecutablePath()); + } + } + + protected class InVmFridaDebuggerProgramLaunchOffer + extends AbstractFridaDebuggerProgramLaunchOffer { + private static final String FACTORY_CLS_NAME = "agent.frida.FridaInJvmDebuggerModelFactory"; + + public InVmFridaDebuggerProgramLaunchOffer(Program program, PluginTool tool, + DebuggerModelFactory factory) { + super(program, tool, factory); + } + + @Override + public String getConfigName() { + return "IN-VM Frida"; + } + + @Override + public String getMenuTitle() { + return "in Frida locally IN-VM"; + } + } + + protected class GadpFridaDebuggerProgramLaunchOffer + extends AbstractFridaDebuggerProgramLaunchOffer { + private static final String FACTORY_CLS_NAME = + "agent.frida.gadp.FridaLocalDebuggerModelFactory"; + + public GadpFridaDebuggerProgramLaunchOffer(Program program, PluginTool tool, + DebuggerModelFactory factory) { + super(program, tool, factory); + } + + @Override + public String getConfigName() { + return "GADP Frida"; + } + + @Override + public String getMenuTitle() { + return "in Frida locally via GADP"; + } + } + + @Override + public Collection getOffers(Program program, PluginTool tool, + DebuggerModelService service) { + String exe = program.getExecutablePath(); + if (exe == null || "".equals(exe.trim())) { + return List.of(); + } + List offers = new ArrayList<>(); + for (DebuggerModelFactory factory : service.getModelFactories()) { + if (!factory.isCompatible()) { + continue; + } + String clsName = factory.getClass().getName(); + if (clsName.equals(InVmFridaDebuggerProgramLaunchOffer.FACTORY_CLS_NAME)) { + offers.add(new InVmFridaDebuggerProgramLaunchOffer(program, tool, factory)); + } + else if (clsName.equals(GadpFridaDebuggerProgramLaunchOffer.FACTORY_CLS_NAME)) { + offers.add(new GadpFridaDebuggerProgramLaunchOffer(program, tool, factory)); + } + } + return offers; + } +} diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/platform/frida/FridaX86DebuggerMappingOpinion.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/platform/frida/FridaX86DebuggerMappingOpinion.java new file mode 100644 index 0000000000..b08c312630 --- /dev/null +++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/platform/frida/FridaX86DebuggerMappingOpinion.java @@ -0,0 +1,126 @@ +/* ### + * IP: GHIDRA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ghidra.app.plugin.core.debug.platform.frida; + +import java.util.Set; + +import ghidra.app.plugin.core.debug.mapping.*; +import ghidra.dbg.target.TargetEnvironment; +import ghidra.dbg.target.TargetProcess; +import ghidra.program.model.lang.CompilerSpecID; +import ghidra.program.model.lang.LanguageID; +import ghidra.util.Msg; + +public class FridaX86DebuggerMappingOpinion implements DebuggerMappingOpinion { + protected static final LanguageID LANG_ID_X86 = new LanguageID("x86:LE:32:default"); + protected static final LanguageID LANG_ID_X86_64 = new LanguageID("x86:LE:64:default"); + protected static final CompilerSpecID COMP_ID_DEFAULT = new CompilerSpecID("default"); + protected static final CompilerSpecID COMP_ID_GCC = new CompilerSpecID("gcc"); + protected static final CompilerSpecID COMP_ID_VS = new CompilerSpecID("windows"); + + protected static class FridaI386MacosOffer extends DefaultDebuggerMappingOffer { + public FridaI386MacosOffer(TargetProcess process) { + super(process, 100, "Frida on macOS i386", LANG_ID_X86, COMP_ID_GCC, Set.of()); + } + } + + protected static class FridaI386LinuxOffer extends DefaultDebuggerMappingOffer { + public FridaI386LinuxOffer(TargetProcess process) { + super(process, 100, "Frida on Linux i386", LANG_ID_X86, COMP_ID_GCC, Set.of()); + } + } + + // TODO + protected static class FridaI386WindowsOffer extends DefaultDebuggerMappingOffer { + public FridaI386WindowsOffer(TargetProcess process) { + super(process, 100, "Frida on Windows i386", LANG_ID_X86, COMP_ID_VS, + Set.of()); + } + } + + protected static class FridaI386X86_64MacosOffer extends DefaultDebuggerMappingOffer { + public FridaI386X86_64MacosOffer(TargetProcess process) { + super(process, 100, "Frida on macOS x86_64", LANG_ID_X86_64, COMP_ID_GCC, Set.of()); + } + } + + protected static class FridaI386X86_64LinuxOffer extends DefaultDebuggerMappingOffer { + public FridaI386X86_64LinuxOffer(TargetProcess process) { + super(process, 100, "Frida on Linux x86_64", LANG_ID_X86_64, COMP_ID_GCC, Set.of()); + } + } + + protected static class FridaI386X86_64WindowsOffer extends DefaultDebuggerMappingOffer { + public FridaI386X86_64WindowsOffer(TargetProcess process) { + super(process, 100, "Frida on Windows x64", LANG_ID_X86_64, COMP_ID_VS, + Set.of()); + } + } + + @Override + public Set offersForEnv(TargetEnvironment env, TargetProcess process, + boolean includeOverrides) { + if (!env.getDebugger().toLowerCase().contains("frida")) { + return Set.of(); + } + String arch = env.getArchitecture(); + boolean is32Bit = arch.contains("ia32") ||arch.contains("x86-32") || arch.contains("i386") || + arch.contains("x86_32"); + boolean is64Bit = arch.contains("x64") ||arch.contains("x86-64") || arch.contains("x64-32") || + arch.contains("x86_64") || arch.contains("x64_32") || arch.contains("i686"); + String os = env.getOperatingSystem(); + if (os.contains("darwin")) { + if (is64Bit) { + Msg.info(this, "Using os=" + os + " arch=" + arch); + return Set.of(new FridaI386X86_64MacosOffer(process)); + } + else if (is32Bit) { + Msg.info(this, "Using os=" + os + " arch=" + arch); + return Set.of(new FridaI386MacosOffer(process)); + } + else { + return Set.of(); + } + } + else if (os.contains("Linux") || os.contains("linux")) { + if (is64Bit) { + Msg.info(this, "Using os=" + os + " arch=" + arch); + return Set.of(new FridaI386X86_64LinuxOffer(process)); + } + else if (is32Bit) { + Msg.info(this, "Using os=" + os + " arch=" + arch); + return Set.of(new FridaI386LinuxOffer(process)); + } + else { + return Set.of(); + } + } + else if (os.contains("windows")) { + if (is64Bit) { + Msg.info(this, "Using os=" + os + " arch=" + arch); + return Set.of(new FridaI386X86_64WindowsOffer(process)); + } + else if (is32Bit) { + Msg.info(this, "Using os=" + os + " arch=" + arch); + return Set.of(new FridaI386WindowsOffer(process)); + } + else { + return Set.of(); + } + } + return Set.of(); + } +} diff --git a/Ghidra/Debug/Framework-Debugging/src/main/java/ghidra/dbg/target/TargetMethod.java b/Ghidra/Debug/Framework-Debugging/src/main/java/ghidra/dbg/target/TargetMethod.java index e6e723b137..9d1de33d12 100644 --- a/Ghidra/Debug/Framework-Debugging/src/main/java/ghidra/dbg/target/TargetMethod.java +++ b/Ghidra/Debug/Framework-Debugging/src/main/java/ghidra/dbg/target/TargetMethod.java @@ -36,6 +36,7 @@ import ghidra.dbg.util.CollectionUtils.AbstractNMap; public interface TargetMethod extends TargetObject { String PARAMETERS_ATTRIBUTE_NAME = PREFIX_INVISIBLE + "parameters"; String RETURN_TYPE_ATTRIBUTE_NAME = PREFIX_INVISIBLE + "return_type"; + public static String REDIRECT = "<="; /** * A description of a method parameter @@ -167,6 +168,7 @@ public interface TargetMethod extends TargetObject { "display='%s' description='%s' choices=%s", name, type, defaultValue, required, display, description, choices); } + } public interface TargetParameterMap extends Map> {