├── .github └── workflows │ └── ci.yml ├── .gitignore ├── MUI ├── GhidraEclipseFormatter.xml ├── Module.manifest ├── build.gradle ├── extension.properties ├── os │ └── linux_x86_64 │ │ ├── muicore_server │ │ └── solc └── src │ └── main │ ├── grpc │ └── muicore │ │ └── ManticoreUIGrpc.java │ ├── java │ ├── mui │ │ ├── MUIHookListComponent.java │ │ ├── MUIHookUserObject.java │ │ ├── MUILogContentComponent.java │ │ ├── MUILogProvider.java │ │ ├── MUILogTabComponent.java │ │ ├── MUIPlugin.java │ │ ├── MUIPopupMenu.java │ │ ├── MUISettings.java │ │ ├── MUISetupProvider.java │ │ ├── MUIStateListProvider.java │ │ └── ManticoreRunner.java │ └── muicore │ │ └── MUICore.java │ └── proto │ └── MUICore.proto ├── MUICore ├── README.md ├── justfile ├── muicore │ ├── MUICore.proto │ ├── MUICore_pb2.py │ ├── MUICore_pb2.pyi │ ├── MUICore_pb2_grpc.py │ ├── __init__.py │ ├── evm_utils.py │ ├── introspect_plugin.py │ ├── mui_server.py │ └── native_utils.py ├── pyproject.toml ├── requirements-dev.txt ├── setup.py └── tests │ ├── __init__.py │ ├── binaries │ └── arguments_linux_amd64 │ ├── contracts │ └── adder.sol │ ├── mock_classes.py │ ├── solc │ ├── test_ethereum.py │ └── test_native.py ├── README.md └── justfile /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | on: 3 | pull_request: 4 | branches: 5 | - '*' 6 | push: 7 | 8 | jobs: 9 | test: 10 | runs-on: ubuntu-20.04 11 | steps: 12 | - uses: actions/checkout@v2 13 | 14 | - uses: actions/setup-java@v2 15 | with: 16 | distribution: 'temurin' 17 | java-version: '11' 18 | 19 | - uses: actions/setup-python@v2 20 | with: 21 | python-version: 3.9 22 | 23 | - name: 'Install tools' 24 | run: | 25 | # just command runner https://github.com/casey/just#pre-built-binaries 26 | curl --proto '=https' --tlsv1.2 -sSf https://just.systems/install.sh | bash -s -- --to "${HOME}/.local/bin" 27 | 28 | - name: 'Setup Ghidra' 29 | run: | 30 | wget https://github.com/NationalSecurityAgency/ghidra/releases/download/Ghidra_10.1.1_build/ghidra_10.1.1_PUBLIC_20211221.zip --output-document=ghidra.zip 31 | unzip ghidra.zip 32 | echo "GHIDRA_INSTALL_DIR=$(pwd)/ghidra_10.1.1_PUBLIC" >> $GITHUB_ENV 33 | 34 | - name: 'Lint MUI' 35 | run: | 36 | pip install -r MUICore/requirements-dev.txt 37 | just lint 38 | 39 | - name: 'Test MUICore' 40 | working-directory: MUICore 41 | run: | 42 | python3 -m venv mui-env 43 | source mui-env/bin/activate 44 | pip install . 45 | just test 46 | 47 | - name: Cleanup Gradle Cache 48 | # Remove some files from the Gradle cache, so they aren't cached by GitHub Actions. 49 | # Restoring these files from a GitHub Actions cache might cause problems for future builds. 50 | run: | 51 | rm -f ~/.gradle/caches/modules-2/modules-2.lock 52 | rm -f ~/.gradle/caches/modules-2/gc.properties 53 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | MUI/bin/ 2 | MUI/build/ 3 | MUI/lib/* 4 | **/.classpath 5 | **/.settings 6 | **/.gradle/ 7 | **/.project 8 | **/.pydevproject 9 | **/__pycache__/ 10 | **/mcore*/ 11 | **/dist/ 12 | **/.mypy_cache/ 13 | -------------------------------------------------------------------------------- /MUI/Module.manifest: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/trailofbits/ManticoreUI-Ghidra/8ba2fa35c131c178512d61e52d6929ed74e1e220/MUI/Module.manifest -------------------------------------------------------------------------------- /MUI/build.gradle: -------------------------------------------------------------------------------- 1 | /* ### 2 | * IP: GHIDRA 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | // Builds a Ghidra Extension for a given Ghidra installation. 17 | // 18 | // An absolute path to the Ghidra installation directory must be supplied either by setting the 19 | // GHIDRA_INSTALL_DIR environment variable or Gradle project property: 20 | // 21 | // > export GHIDRA_INSTALL_DIR= 22 | // > gradle 23 | // 24 | // or 25 | // 26 | // > gradle -PGHIDRA_INSTALL_DIR= 27 | // 28 | // Gradle should be invoked from the directory of the project to build. Please see the 29 | // application.gradle.version property in /Ghidra/application.properties 30 | // for the correction version of Gradle to use for the Ghidra installation you specify. 31 | 32 | plugins { 33 | id "com.diffplug.spotless" version "6.1.0" 34 | id "com.google.protobuf" version "0.8.18" 35 | id "java" 36 | } 37 | 38 | //----------------------START "DO NOT MODIFY" SECTION------------------------------ 39 | def ghidraInstallDir 40 | 41 | if (System.env.GHIDRA_INSTALL_DIR) { 42 | ghidraInstallDir = System.env.GHIDRA_INSTALL_DIR 43 | } 44 | else if (project.hasProperty("GHIDRA_INSTALL_DIR")) { 45 | ghidraInstallDir = project.getProperty("GHIDRA_INSTALL_DIR") 46 | } 47 | 48 | if (ghidraInstallDir) { 49 | apply from: new File(ghidraInstallDir).getCanonicalPath() + "/support/buildExtension.gradle" 50 | } 51 | else { 52 | throw new GradleException("GHIDRA_INSTALL_DIR is not defined!") 53 | } 54 | //----------------------END "DO NOT MODIFY" SECTION------------------------------- 55 | 56 | 57 | def ghidraDir = file(ghidraInstallDir + "/Ghidra").getCanonicalFile().getAbsolutePath() 58 | def ghidraProps = new Properties() 59 | def ghidraVersion 60 | def ghidraUserDirectory 61 | def ghidraReleaseName 62 | 63 | 64 | file(ghidraDir + "/application.properties").withReader { reader -> 65 | ghidraProps.load(reader) 66 | ghidraReleaseName = ghidraProps.getProperty('application.release.name') 67 | ghidraVersion = ghidraProps.getProperty('application.version') 68 | ghidraUserDirectory = "${project.gradle.gradleUserHomeDir.parent}/.ghidra/.ghidra_${ghidra_version}_${ghidraReleaseName}" 69 | } 70 | 71 | def userDir = new File(ghidraUserDirectory) 72 | if(!userDir.exists()){ 73 | userDir.mkdirs() 74 | } 75 | 76 | 77 | repositories { 78 | maven { url "https://maven-central.storage-download.googleapis.com/maven2/" } 79 | mavenCentral() 80 | } 81 | 82 | 83 | def grpcVersion = '1.44.1' 84 | def protobufVersion = '3.19.2' 85 | def protocVersion = protobufVersion 86 | 87 | 88 | dependencies { 89 | implementation "io.grpc:grpc-protobuf:${grpcVersion}" 90 | implementation "io.grpc:grpc-stub:${grpcVersion}" 91 | compileOnly "org.apache.tomcat:annotations-api:6.0.53" 92 | 93 | // examples/advanced need this for JsonFormat 94 | implementation "com.google.protobuf:protobuf-java-util:${protobufVersion}" 95 | 96 | runtimeOnly "io.grpc:grpc-netty-shaded:${grpcVersion}" 97 | 98 | testImplementation "io.grpc:grpc-testing:${grpcVersion}" 99 | } 100 | 101 | protobuf { 102 | generatedFilesBaseDir = "$projectDir/src" 103 | protoc { 104 | artifact = "com.google.protobuf:protoc:${protocVersion}" 105 | } 106 | plugins { 107 | grpc { 108 | artifact = "io.grpc:protoc-gen-grpc-java:${grpcVersion}" 109 | } 110 | } 111 | generateProtoTasks { 112 | all()*.plugins { 113 | grpc {} 114 | } 115 | } 116 | } 117 | 118 | tasks.whenTaskAdded { task -> 119 | if (task.name == 'extractIncludeProto'){ 120 | task.getDependsOn().add(copyDependencies) 121 | } else if (task.name == 'generateProto'){ 122 | zipSource.dependsOn task 123 | processResources.dependsOn task 124 | spotlessJava.dependsOn task 125 | } 126 | } 127 | 128 | // The following installation helpers were copied from 129 | // https://github.com/cmu-sei/kaiju/blob/c00a351aa4332e8b6e2be94f6fe52418f8946b11/build.gradle 130 | task uninstallPreviousMUI { 131 | description = "Removes a previous MUI installation, or notifies user of previous installation" 132 | doFirst { 133 | // check if a previous MUI installation exists 134 | if (new File(ghidraUserDirectory + '/Extensions/MUI').exists()) { 135 | // automatically remove past install if user specifies 136 | if (project.hasProperty('MUI_AUTO_REMOVE')) { 137 | def muiDir = new File(ghidraUserDirectory + '/Extensions/MUI/') 138 | muiDir.deleteDir() 139 | logger.quiet("SUCCESS! Previous standalone MUI installation was removed automatically by gradle.") 140 | } else { 141 | throw new GradleException("FATAL ERROR! A previous installation of MUI was detected. It is recommended that you either manually remove this installation before re-installing MUI, or re-run this gradle installation script with the -PMUI_AUTO_REMOVE option.") 142 | } 143 | } 144 | } 145 | outputs.upToDateWhen { false } // ensures never cached, always runs if user enables 146 | } 147 | 148 | build.dependsOn uninstallPreviousMUI 149 | 150 | 151 | task installZip(type: Exec) { 152 | dependsOn buildExtension 153 | group = "Installation" 154 | description = "Installs the zip package in installed Ghidra's default extension directory" 155 | def zipPath = buildExtension.destinationDirectory.get().getAsFile().toString() + "/" + buildExtension.archiveBaseName.get() + "." + buildExtension.archiveExtension.get() 156 | commandLine 'unzip', '-o', zipPath, '-d', ghidraUserDirectory+'/Extensions/' 157 | } 158 | 159 | task install() { 160 | dependsOn installZip 161 | group = "Installation" 162 | description = "Alternate name for installZip task" 163 | //title = "Install zip" 164 | doLast { 165 | // Empty action list 166 | logger.quiet("Installed Trail of Bits MUI from zip!") 167 | } 168 | } 169 | // End copy 170 | 171 | // Code style formatting 172 | spotless { 173 | // optional: limit format enforcement to just the files changed by this feature branch 174 | // ratchetFrom 'origin/main' 175 | 176 | format 'misc', { 177 | // define the files to apply `misc` to 178 | target '*.gradle', '*.md', '.gitignore' 179 | 180 | // define the steps to apply to those files 181 | trimTrailingWhitespace() 182 | indentWithSpaces(4) 183 | endWithNewline() 184 | } 185 | java { 186 | target 'src/*/java/mui/**/*.java', 'ghidra_scripts/*.java' 187 | targetExclude('src/main/java/muicore/**/*.java') 188 | targetExclude('src/main/grpc/**/*.java') 189 | // The Ghidra formatting file was taken from Ghidra 10.1.1 located in 190 | // eclipse/GhidraEclipseFormatter.xml Ghidra repo 191 | eclipse().configFile('GhidraEclipseFormatter.xml') 192 | } 193 | } 194 | -------------------------------------------------------------------------------- /MUI/extension.properties: -------------------------------------------------------------------------------- 1 | name=@extname@ 2 | description=MUI-Ghidra 3 | author= 4 | createdOn= 5 | version=@extversion@ 6 | -------------------------------------------------------------------------------- /MUI/os/linux_x86_64/muicore_server: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/trailofbits/ManticoreUI-Ghidra/8ba2fa35c131c178512d61e52d6929ed74e1e220/MUI/os/linux_x86_64/muicore_server -------------------------------------------------------------------------------- /MUI/os/linux_x86_64/solc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/trailofbits/ManticoreUI-Ghidra/8ba2fa35c131c178512d61e52d6929ed74e1e220/MUI/os/linux_x86_64/solc -------------------------------------------------------------------------------- /MUI/src/main/grpc/muicore/ManticoreUIGrpc.java: -------------------------------------------------------------------------------- 1 | package muicore; 2 | 3 | import static io.grpc.MethodDescriptor.generateFullMethodName; 4 | 5 | /** 6 | */ 7 | @javax.annotation.Generated( 8 | value = "by gRPC proto compiler (version 1.44.1)", 9 | comments = "Source: MUICore.proto") 10 | @io.grpc.stub.annotations.GrpcGenerated 11 | public final class ManticoreUIGrpc { 12 | 13 | private ManticoreUIGrpc() {} 14 | 15 | public static final String SERVICE_NAME = "muicore.ManticoreUI"; 16 | 17 | // Static method descriptors that strictly reflect the proto. 18 | private static volatile io.grpc.MethodDescriptor getStartNativeMethod; 20 | 21 | @io.grpc.stub.annotations.RpcMethod( 22 | fullMethodName = SERVICE_NAME + '/' + "StartNative", 23 | requestType = muicore.MUICore.NativeArguments.class, 24 | responseType = muicore.MUICore.ManticoreInstance.class, 25 | methodType = io.grpc.MethodDescriptor.MethodType.UNARY) 26 | public static io.grpc.MethodDescriptor getStartNativeMethod() { 28 | io.grpc.MethodDescriptor getStartNativeMethod; 29 | if ((getStartNativeMethod = ManticoreUIGrpc.getStartNativeMethod) == null) { 30 | synchronized (ManticoreUIGrpc.class) { 31 | if ((getStartNativeMethod = ManticoreUIGrpc.getStartNativeMethod) == null) { 32 | ManticoreUIGrpc.getStartNativeMethod = getStartNativeMethod = 33 | io.grpc.MethodDescriptor.newBuilder() 34 | .setType(io.grpc.MethodDescriptor.MethodType.UNARY) 35 | .setFullMethodName(generateFullMethodName(SERVICE_NAME, "StartNative")) 36 | .setSampledToLocalTracing(true) 37 | .setRequestMarshaller(io.grpc.protobuf.ProtoUtils.marshaller( 38 | muicore.MUICore.NativeArguments.getDefaultInstance())) 39 | .setResponseMarshaller(io.grpc.protobuf.ProtoUtils.marshaller( 40 | muicore.MUICore.ManticoreInstance.getDefaultInstance())) 41 | .setSchemaDescriptor(new ManticoreUIMethodDescriptorSupplier("StartNative")) 42 | .build(); 43 | } 44 | } 45 | } 46 | return getStartNativeMethod; 47 | } 48 | 49 | private static volatile io.grpc.MethodDescriptor getStartEVMMethod; 51 | 52 | @io.grpc.stub.annotations.RpcMethod( 53 | fullMethodName = SERVICE_NAME + '/' + "StartEVM", 54 | requestType = muicore.MUICore.EVMArguments.class, 55 | responseType = muicore.MUICore.ManticoreInstance.class, 56 | methodType = io.grpc.MethodDescriptor.MethodType.UNARY) 57 | public static io.grpc.MethodDescriptor getStartEVMMethod() { 59 | io.grpc.MethodDescriptor getStartEVMMethod; 60 | if ((getStartEVMMethod = ManticoreUIGrpc.getStartEVMMethod) == null) { 61 | synchronized (ManticoreUIGrpc.class) { 62 | if ((getStartEVMMethod = ManticoreUIGrpc.getStartEVMMethod) == null) { 63 | ManticoreUIGrpc.getStartEVMMethod = getStartEVMMethod = 64 | io.grpc.MethodDescriptor.newBuilder() 65 | .setType(io.grpc.MethodDescriptor.MethodType.UNARY) 66 | .setFullMethodName(generateFullMethodName(SERVICE_NAME, "StartEVM")) 67 | .setSampledToLocalTracing(true) 68 | .setRequestMarshaller(io.grpc.protobuf.ProtoUtils.marshaller( 69 | muicore.MUICore.EVMArguments.getDefaultInstance())) 70 | .setResponseMarshaller(io.grpc.protobuf.ProtoUtils.marshaller( 71 | muicore.MUICore.ManticoreInstance.getDefaultInstance())) 72 | .setSchemaDescriptor(new ManticoreUIMethodDescriptorSupplier("StartEVM")) 73 | .build(); 74 | } 75 | } 76 | } 77 | return getStartEVMMethod; 78 | } 79 | 80 | private static volatile io.grpc.MethodDescriptor getTerminateMethod; 82 | 83 | @io.grpc.stub.annotations.RpcMethod( 84 | fullMethodName = SERVICE_NAME + '/' + "Terminate", 85 | requestType = muicore.MUICore.ManticoreInstance.class, 86 | responseType = muicore.MUICore.TerminateResponse.class, 87 | methodType = io.grpc.MethodDescriptor.MethodType.UNARY) 88 | public static io.grpc.MethodDescriptor getTerminateMethod() { 90 | io.grpc.MethodDescriptor getTerminateMethod; 91 | if ((getTerminateMethod = ManticoreUIGrpc.getTerminateMethod) == null) { 92 | synchronized (ManticoreUIGrpc.class) { 93 | if ((getTerminateMethod = ManticoreUIGrpc.getTerminateMethod) == null) { 94 | ManticoreUIGrpc.getTerminateMethod = getTerminateMethod = 95 | io.grpc.MethodDescriptor.newBuilder() 96 | .setType(io.grpc.MethodDescriptor.MethodType.UNARY) 97 | .setFullMethodName(generateFullMethodName(SERVICE_NAME, "Terminate")) 98 | .setSampledToLocalTracing(true) 99 | .setRequestMarshaller(io.grpc.protobuf.ProtoUtils.marshaller( 100 | muicore.MUICore.ManticoreInstance.getDefaultInstance())) 101 | .setResponseMarshaller(io.grpc.protobuf.ProtoUtils.marshaller( 102 | muicore.MUICore.TerminateResponse.getDefaultInstance())) 103 | .setSchemaDescriptor(new ManticoreUIMethodDescriptorSupplier("Terminate")) 104 | .build(); 105 | } 106 | } 107 | } 108 | return getTerminateMethod; 109 | } 110 | 111 | private static volatile io.grpc.MethodDescriptor getGetStateListMethod; 113 | 114 | @io.grpc.stub.annotations.RpcMethod( 115 | fullMethodName = SERVICE_NAME + '/' + "GetStateList", 116 | requestType = muicore.MUICore.ManticoreInstance.class, 117 | responseType = muicore.MUICore.MUIStateList.class, 118 | methodType = io.grpc.MethodDescriptor.MethodType.UNARY) 119 | public static io.grpc.MethodDescriptor getGetStateListMethod() { 121 | io.grpc.MethodDescriptor getGetStateListMethod; 122 | if ((getGetStateListMethod = ManticoreUIGrpc.getGetStateListMethod) == null) { 123 | synchronized (ManticoreUIGrpc.class) { 124 | if ((getGetStateListMethod = ManticoreUIGrpc.getGetStateListMethod) == null) { 125 | ManticoreUIGrpc.getGetStateListMethod = getGetStateListMethod = 126 | io.grpc.MethodDescriptor.newBuilder() 127 | .setType(io.grpc.MethodDescriptor.MethodType.UNARY) 128 | .setFullMethodName(generateFullMethodName(SERVICE_NAME, "GetStateList")) 129 | .setSampledToLocalTracing(true) 130 | .setRequestMarshaller(io.grpc.protobuf.ProtoUtils.marshaller( 131 | muicore.MUICore.ManticoreInstance.getDefaultInstance())) 132 | .setResponseMarshaller(io.grpc.protobuf.ProtoUtils.marshaller( 133 | muicore.MUICore.MUIStateList.getDefaultInstance())) 134 | .setSchemaDescriptor(new ManticoreUIMethodDescriptorSupplier("GetStateList")) 135 | .build(); 136 | } 137 | } 138 | } 139 | return getGetStateListMethod; 140 | } 141 | 142 | private static volatile io.grpc.MethodDescriptor getGetMessageListMethod; 144 | 145 | @io.grpc.stub.annotations.RpcMethod( 146 | fullMethodName = SERVICE_NAME + '/' + "GetMessageList", 147 | requestType = muicore.MUICore.ManticoreInstance.class, 148 | responseType = muicore.MUICore.MUIMessageList.class, 149 | methodType = io.grpc.MethodDescriptor.MethodType.UNARY) 150 | public static io.grpc.MethodDescriptor getGetMessageListMethod() { 152 | io.grpc.MethodDescriptor getGetMessageListMethod; 153 | if ((getGetMessageListMethod = ManticoreUIGrpc.getGetMessageListMethod) == null) { 154 | synchronized (ManticoreUIGrpc.class) { 155 | if ((getGetMessageListMethod = ManticoreUIGrpc.getGetMessageListMethod) == null) { 156 | ManticoreUIGrpc.getGetMessageListMethod = getGetMessageListMethod = 157 | io.grpc.MethodDescriptor.newBuilder() 158 | .setType(io.grpc.MethodDescriptor.MethodType.UNARY) 159 | .setFullMethodName(generateFullMethodName(SERVICE_NAME, "GetMessageList")) 160 | .setSampledToLocalTracing(true) 161 | .setRequestMarshaller(io.grpc.protobuf.ProtoUtils.marshaller( 162 | muicore.MUICore.ManticoreInstance.getDefaultInstance())) 163 | .setResponseMarshaller(io.grpc.protobuf.ProtoUtils.marshaller( 164 | muicore.MUICore.MUIMessageList.getDefaultInstance())) 165 | .setSchemaDescriptor(new ManticoreUIMethodDescriptorSupplier("GetMessageList")) 166 | .build(); 167 | } 168 | } 169 | } 170 | return getGetMessageListMethod; 171 | } 172 | 173 | private static volatile io.grpc.MethodDescriptor getCheckManticoreRunningMethod; 175 | 176 | @io.grpc.stub.annotations.RpcMethod( 177 | fullMethodName = SERVICE_NAME + '/' + "CheckManticoreRunning", 178 | requestType = muicore.MUICore.ManticoreInstance.class, 179 | responseType = muicore.MUICore.ManticoreRunningStatus.class, 180 | methodType = io.grpc.MethodDescriptor.MethodType.UNARY) 181 | public static io.grpc.MethodDescriptor getCheckManticoreRunningMethod() { 183 | io.grpc.MethodDescriptor getCheckManticoreRunningMethod; 184 | if ((getCheckManticoreRunningMethod = ManticoreUIGrpc.getCheckManticoreRunningMethod) == null) { 185 | synchronized (ManticoreUIGrpc.class) { 186 | if ((getCheckManticoreRunningMethod = ManticoreUIGrpc.getCheckManticoreRunningMethod) == null) { 187 | ManticoreUIGrpc.getCheckManticoreRunningMethod = getCheckManticoreRunningMethod = 188 | io.grpc.MethodDescriptor.newBuilder() 189 | .setType(io.grpc.MethodDescriptor.MethodType.UNARY) 190 | .setFullMethodName(generateFullMethodName(SERVICE_NAME, "CheckManticoreRunning")) 191 | .setSampledToLocalTracing(true) 192 | .setRequestMarshaller(io.grpc.protobuf.ProtoUtils.marshaller( 193 | muicore.MUICore.ManticoreInstance.getDefaultInstance())) 194 | .setResponseMarshaller(io.grpc.protobuf.ProtoUtils.marshaller( 195 | muicore.MUICore.ManticoreRunningStatus.getDefaultInstance())) 196 | .setSchemaDescriptor(new ManticoreUIMethodDescriptorSupplier("CheckManticoreRunning")) 197 | .build(); 198 | } 199 | } 200 | } 201 | return getCheckManticoreRunningMethod; 202 | } 203 | 204 | private static volatile io.grpc.MethodDescriptor getStopServerMethod; 206 | 207 | @io.grpc.stub.annotations.RpcMethod( 208 | fullMethodName = SERVICE_NAME + '/' + "StopServer", 209 | requestType = muicore.MUICore.StopServerRequest.class, 210 | responseType = muicore.MUICore.StopServerResponse.class, 211 | methodType = io.grpc.MethodDescriptor.MethodType.UNARY) 212 | public static io.grpc.MethodDescriptor getStopServerMethod() { 214 | io.grpc.MethodDescriptor getStopServerMethod; 215 | if ((getStopServerMethod = ManticoreUIGrpc.getStopServerMethod) == null) { 216 | synchronized (ManticoreUIGrpc.class) { 217 | if ((getStopServerMethod = ManticoreUIGrpc.getStopServerMethod) == null) { 218 | ManticoreUIGrpc.getStopServerMethod = getStopServerMethod = 219 | io.grpc.MethodDescriptor.newBuilder() 220 | .setType(io.grpc.MethodDescriptor.MethodType.UNARY) 221 | .setFullMethodName(generateFullMethodName(SERVICE_NAME, "StopServer")) 222 | .setSampledToLocalTracing(true) 223 | .setRequestMarshaller(io.grpc.protobuf.ProtoUtils.marshaller( 224 | muicore.MUICore.StopServerRequest.getDefaultInstance())) 225 | .setResponseMarshaller(io.grpc.protobuf.ProtoUtils.marshaller( 226 | muicore.MUICore.StopServerResponse.getDefaultInstance())) 227 | .setSchemaDescriptor(new ManticoreUIMethodDescriptorSupplier("StopServer")) 228 | .build(); 229 | } 230 | } 231 | } 232 | return getStopServerMethod; 233 | } 234 | 235 | /** 236 | * Creates a new async stub that supports all call types for the service 237 | */ 238 | public static ManticoreUIStub newStub(io.grpc.Channel channel) { 239 | io.grpc.stub.AbstractStub.StubFactory factory = 240 | new io.grpc.stub.AbstractStub.StubFactory() { 241 | @java.lang.Override 242 | public ManticoreUIStub newStub(io.grpc.Channel channel, io.grpc.CallOptions callOptions) { 243 | return new ManticoreUIStub(channel, callOptions); 244 | } 245 | }; 246 | return ManticoreUIStub.newStub(factory, channel); 247 | } 248 | 249 | /** 250 | * Creates a new blocking-style stub that supports unary and streaming output calls on the service 251 | */ 252 | public static ManticoreUIBlockingStub newBlockingStub( 253 | io.grpc.Channel channel) { 254 | io.grpc.stub.AbstractStub.StubFactory factory = 255 | new io.grpc.stub.AbstractStub.StubFactory() { 256 | @java.lang.Override 257 | public ManticoreUIBlockingStub newStub(io.grpc.Channel channel, io.grpc.CallOptions callOptions) { 258 | return new ManticoreUIBlockingStub(channel, callOptions); 259 | } 260 | }; 261 | return ManticoreUIBlockingStub.newStub(factory, channel); 262 | } 263 | 264 | /** 265 | * Creates a new ListenableFuture-style stub that supports unary calls on the service 266 | */ 267 | public static ManticoreUIFutureStub newFutureStub( 268 | io.grpc.Channel channel) { 269 | io.grpc.stub.AbstractStub.StubFactory factory = 270 | new io.grpc.stub.AbstractStub.StubFactory() { 271 | @java.lang.Override 272 | public ManticoreUIFutureStub newStub(io.grpc.Channel channel, io.grpc.CallOptions callOptions) { 273 | return new ManticoreUIFutureStub(channel, callOptions); 274 | } 275 | }; 276 | return ManticoreUIFutureStub.newStub(factory, channel); 277 | } 278 | 279 | /** 280 | */ 281 | public static abstract class ManticoreUIImplBase implements io.grpc.BindableService { 282 | 283 | /** 284 | */ 285 | public void startNative(muicore.MUICore.NativeArguments request, 286 | io.grpc.stub.StreamObserver responseObserver) { 287 | io.grpc.stub.ServerCalls.asyncUnimplementedUnaryCall(getStartNativeMethod(), responseObserver); 288 | } 289 | 290 | /** 291 | */ 292 | public void startEVM(muicore.MUICore.EVMArguments request, 293 | io.grpc.stub.StreamObserver responseObserver) { 294 | io.grpc.stub.ServerCalls.asyncUnimplementedUnaryCall(getStartEVMMethod(), responseObserver); 295 | } 296 | 297 | /** 298 | */ 299 | public void terminate(muicore.MUICore.ManticoreInstance request, 300 | io.grpc.stub.StreamObserver responseObserver) { 301 | io.grpc.stub.ServerCalls.asyncUnimplementedUnaryCall(getTerminateMethod(), responseObserver); 302 | } 303 | 304 | /** 305 | */ 306 | public void getStateList(muicore.MUICore.ManticoreInstance request, 307 | io.grpc.stub.StreamObserver responseObserver) { 308 | io.grpc.stub.ServerCalls.asyncUnimplementedUnaryCall(getGetStateListMethod(), responseObserver); 309 | } 310 | 311 | /** 312 | */ 313 | public void getMessageList(muicore.MUICore.ManticoreInstance request, 314 | io.grpc.stub.StreamObserver responseObserver) { 315 | io.grpc.stub.ServerCalls.asyncUnimplementedUnaryCall(getGetMessageListMethod(), responseObserver); 316 | } 317 | 318 | /** 319 | */ 320 | public void checkManticoreRunning(muicore.MUICore.ManticoreInstance request, 321 | io.grpc.stub.StreamObserver responseObserver) { 322 | io.grpc.stub.ServerCalls.asyncUnimplementedUnaryCall(getCheckManticoreRunningMethod(), responseObserver); 323 | } 324 | 325 | /** 326 | */ 327 | public void stopServer(muicore.MUICore.StopServerRequest request, 328 | io.grpc.stub.StreamObserver responseObserver) { 329 | io.grpc.stub.ServerCalls.asyncUnimplementedUnaryCall(getStopServerMethod(), responseObserver); 330 | } 331 | 332 | @java.lang.Override public final io.grpc.ServerServiceDefinition bindService() { 333 | return io.grpc.ServerServiceDefinition.builder(getServiceDescriptor()) 334 | .addMethod( 335 | getStartNativeMethod(), 336 | io.grpc.stub.ServerCalls.asyncUnaryCall( 337 | new MethodHandlers< 338 | muicore.MUICore.NativeArguments, 339 | muicore.MUICore.ManticoreInstance>( 340 | this, METHODID_START_NATIVE))) 341 | .addMethod( 342 | getStartEVMMethod(), 343 | io.grpc.stub.ServerCalls.asyncUnaryCall( 344 | new MethodHandlers< 345 | muicore.MUICore.EVMArguments, 346 | muicore.MUICore.ManticoreInstance>( 347 | this, METHODID_START_EVM))) 348 | .addMethod( 349 | getTerminateMethod(), 350 | io.grpc.stub.ServerCalls.asyncUnaryCall( 351 | new MethodHandlers< 352 | muicore.MUICore.ManticoreInstance, 353 | muicore.MUICore.TerminateResponse>( 354 | this, METHODID_TERMINATE))) 355 | .addMethod( 356 | getGetStateListMethod(), 357 | io.grpc.stub.ServerCalls.asyncUnaryCall( 358 | new MethodHandlers< 359 | muicore.MUICore.ManticoreInstance, 360 | muicore.MUICore.MUIStateList>( 361 | this, METHODID_GET_STATE_LIST))) 362 | .addMethod( 363 | getGetMessageListMethod(), 364 | io.grpc.stub.ServerCalls.asyncUnaryCall( 365 | new MethodHandlers< 366 | muicore.MUICore.ManticoreInstance, 367 | muicore.MUICore.MUIMessageList>( 368 | this, METHODID_GET_MESSAGE_LIST))) 369 | .addMethod( 370 | getCheckManticoreRunningMethod(), 371 | io.grpc.stub.ServerCalls.asyncUnaryCall( 372 | new MethodHandlers< 373 | muicore.MUICore.ManticoreInstance, 374 | muicore.MUICore.ManticoreRunningStatus>( 375 | this, METHODID_CHECK_MANTICORE_RUNNING))) 376 | .addMethod( 377 | getStopServerMethod(), 378 | io.grpc.stub.ServerCalls.asyncUnaryCall( 379 | new MethodHandlers< 380 | muicore.MUICore.StopServerRequest, 381 | muicore.MUICore.StopServerResponse>( 382 | this, METHODID_STOP_SERVER))) 383 | .build(); 384 | } 385 | } 386 | 387 | /** 388 | */ 389 | public static final class ManticoreUIStub extends io.grpc.stub.AbstractAsyncStub { 390 | private ManticoreUIStub( 391 | io.grpc.Channel channel, io.grpc.CallOptions callOptions) { 392 | super(channel, callOptions); 393 | } 394 | 395 | @java.lang.Override 396 | protected ManticoreUIStub build( 397 | io.grpc.Channel channel, io.grpc.CallOptions callOptions) { 398 | return new ManticoreUIStub(channel, callOptions); 399 | } 400 | 401 | /** 402 | */ 403 | public void startNative(muicore.MUICore.NativeArguments request, 404 | io.grpc.stub.StreamObserver responseObserver) { 405 | io.grpc.stub.ClientCalls.asyncUnaryCall( 406 | getChannel().newCall(getStartNativeMethod(), getCallOptions()), request, responseObserver); 407 | } 408 | 409 | /** 410 | */ 411 | public void startEVM(muicore.MUICore.EVMArguments request, 412 | io.grpc.stub.StreamObserver responseObserver) { 413 | io.grpc.stub.ClientCalls.asyncUnaryCall( 414 | getChannel().newCall(getStartEVMMethod(), getCallOptions()), request, responseObserver); 415 | } 416 | 417 | /** 418 | */ 419 | public void terminate(muicore.MUICore.ManticoreInstance request, 420 | io.grpc.stub.StreamObserver responseObserver) { 421 | io.grpc.stub.ClientCalls.asyncUnaryCall( 422 | getChannel().newCall(getTerminateMethod(), getCallOptions()), request, responseObserver); 423 | } 424 | 425 | /** 426 | */ 427 | public void getStateList(muicore.MUICore.ManticoreInstance request, 428 | io.grpc.stub.StreamObserver responseObserver) { 429 | io.grpc.stub.ClientCalls.asyncUnaryCall( 430 | getChannel().newCall(getGetStateListMethod(), getCallOptions()), request, responseObserver); 431 | } 432 | 433 | /** 434 | */ 435 | public void getMessageList(muicore.MUICore.ManticoreInstance request, 436 | io.grpc.stub.StreamObserver responseObserver) { 437 | io.grpc.stub.ClientCalls.asyncUnaryCall( 438 | getChannel().newCall(getGetMessageListMethod(), getCallOptions()), request, responseObserver); 439 | } 440 | 441 | /** 442 | */ 443 | public void checkManticoreRunning(muicore.MUICore.ManticoreInstance request, 444 | io.grpc.stub.StreamObserver responseObserver) { 445 | io.grpc.stub.ClientCalls.asyncUnaryCall( 446 | getChannel().newCall(getCheckManticoreRunningMethod(), getCallOptions()), request, responseObserver); 447 | } 448 | 449 | /** 450 | */ 451 | public void stopServer(muicore.MUICore.StopServerRequest request, 452 | io.grpc.stub.StreamObserver responseObserver) { 453 | io.grpc.stub.ClientCalls.asyncUnaryCall( 454 | getChannel().newCall(getStopServerMethod(), getCallOptions()), request, responseObserver); 455 | } 456 | } 457 | 458 | /** 459 | */ 460 | public static final class ManticoreUIBlockingStub extends io.grpc.stub.AbstractBlockingStub { 461 | private ManticoreUIBlockingStub( 462 | io.grpc.Channel channel, io.grpc.CallOptions callOptions) { 463 | super(channel, callOptions); 464 | } 465 | 466 | @java.lang.Override 467 | protected ManticoreUIBlockingStub build( 468 | io.grpc.Channel channel, io.grpc.CallOptions callOptions) { 469 | return new ManticoreUIBlockingStub(channel, callOptions); 470 | } 471 | 472 | /** 473 | */ 474 | public muicore.MUICore.ManticoreInstance startNative(muicore.MUICore.NativeArguments request) { 475 | return io.grpc.stub.ClientCalls.blockingUnaryCall( 476 | getChannel(), getStartNativeMethod(), getCallOptions(), request); 477 | } 478 | 479 | /** 480 | */ 481 | public muicore.MUICore.ManticoreInstance startEVM(muicore.MUICore.EVMArguments request) { 482 | return io.grpc.stub.ClientCalls.blockingUnaryCall( 483 | getChannel(), getStartEVMMethod(), getCallOptions(), request); 484 | } 485 | 486 | /** 487 | */ 488 | public muicore.MUICore.TerminateResponse terminate(muicore.MUICore.ManticoreInstance request) { 489 | return io.grpc.stub.ClientCalls.blockingUnaryCall( 490 | getChannel(), getTerminateMethod(), getCallOptions(), request); 491 | } 492 | 493 | /** 494 | */ 495 | public muicore.MUICore.MUIStateList getStateList(muicore.MUICore.ManticoreInstance request) { 496 | return io.grpc.stub.ClientCalls.blockingUnaryCall( 497 | getChannel(), getGetStateListMethod(), getCallOptions(), request); 498 | } 499 | 500 | /** 501 | */ 502 | public muicore.MUICore.MUIMessageList getMessageList(muicore.MUICore.ManticoreInstance request) { 503 | return io.grpc.stub.ClientCalls.blockingUnaryCall( 504 | getChannel(), getGetMessageListMethod(), getCallOptions(), request); 505 | } 506 | 507 | /** 508 | */ 509 | public muicore.MUICore.ManticoreRunningStatus checkManticoreRunning(muicore.MUICore.ManticoreInstance request) { 510 | return io.grpc.stub.ClientCalls.blockingUnaryCall( 511 | getChannel(), getCheckManticoreRunningMethod(), getCallOptions(), request); 512 | } 513 | 514 | /** 515 | */ 516 | public muicore.MUICore.StopServerResponse stopServer(muicore.MUICore.StopServerRequest request) { 517 | return io.grpc.stub.ClientCalls.blockingUnaryCall( 518 | getChannel(), getStopServerMethod(), getCallOptions(), request); 519 | } 520 | } 521 | 522 | /** 523 | */ 524 | public static final class ManticoreUIFutureStub extends io.grpc.stub.AbstractFutureStub { 525 | private ManticoreUIFutureStub( 526 | io.grpc.Channel channel, io.grpc.CallOptions callOptions) { 527 | super(channel, callOptions); 528 | } 529 | 530 | @java.lang.Override 531 | protected ManticoreUIFutureStub build( 532 | io.grpc.Channel channel, io.grpc.CallOptions callOptions) { 533 | return new ManticoreUIFutureStub(channel, callOptions); 534 | } 535 | 536 | /** 537 | */ 538 | public com.google.common.util.concurrent.ListenableFuture startNative( 539 | muicore.MUICore.NativeArguments request) { 540 | return io.grpc.stub.ClientCalls.futureUnaryCall( 541 | getChannel().newCall(getStartNativeMethod(), getCallOptions()), request); 542 | } 543 | 544 | /** 545 | */ 546 | public com.google.common.util.concurrent.ListenableFuture startEVM( 547 | muicore.MUICore.EVMArguments request) { 548 | return io.grpc.stub.ClientCalls.futureUnaryCall( 549 | getChannel().newCall(getStartEVMMethod(), getCallOptions()), request); 550 | } 551 | 552 | /** 553 | */ 554 | public com.google.common.util.concurrent.ListenableFuture terminate( 555 | muicore.MUICore.ManticoreInstance request) { 556 | return io.grpc.stub.ClientCalls.futureUnaryCall( 557 | getChannel().newCall(getTerminateMethod(), getCallOptions()), request); 558 | } 559 | 560 | /** 561 | */ 562 | public com.google.common.util.concurrent.ListenableFuture getStateList( 563 | muicore.MUICore.ManticoreInstance request) { 564 | return io.grpc.stub.ClientCalls.futureUnaryCall( 565 | getChannel().newCall(getGetStateListMethod(), getCallOptions()), request); 566 | } 567 | 568 | /** 569 | */ 570 | public com.google.common.util.concurrent.ListenableFuture getMessageList( 571 | muicore.MUICore.ManticoreInstance request) { 572 | return io.grpc.stub.ClientCalls.futureUnaryCall( 573 | getChannel().newCall(getGetMessageListMethod(), getCallOptions()), request); 574 | } 575 | 576 | /** 577 | */ 578 | public com.google.common.util.concurrent.ListenableFuture checkManticoreRunning( 579 | muicore.MUICore.ManticoreInstance request) { 580 | return io.grpc.stub.ClientCalls.futureUnaryCall( 581 | getChannel().newCall(getCheckManticoreRunningMethod(), getCallOptions()), request); 582 | } 583 | 584 | /** 585 | */ 586 | public com.google.common.util.concurrent.ListenableFuture stopServer( 587 | muicore.MUICore.StopServerRequest request) { 588 | return io.grpc.stub.ClientCalls.futureUnaryCall( 589 | getChannel().newCall(getStopServerMethod(), getCallOptions()), request); 590 | } 591 | } 592 | 593 | private static final int METHODID_START_NATIVE = 0; 594 | private static final int METHODID_START_EVM = 1; 595 | private static final int METHODID_TERMINATE = 2; 596 | private static final int METHODID_GET_STATE_LIST = 3; 597 | private static final int METHODID_GET_MESSAGE_LIST = 4; 598 | private static final int METHODID_CHECK_MANTICORE_RUNNING = 5; 599 | private static final int METHODID_STOP_SERVER = 6; 600 | 601 | private static final class MethodHandlers implements 602 | io.grpc.stub.ServerCalls.UnaryMethod, 603 | io.grpc.stub.ServerCalls.ServerStreamingMethod, 604 | io.grpc.stub.ServerCalls.ClientStreamingMethod, 605 | io.grpc.stub.ServerCalls.BidiStreamingMethod { 606 | private final ManticoreUIImplBase serviceImpl; 607 | private final int methodId; 608 | 609 | MethodHandlers(ManticoreUIImplBase serviceImpl, int methodId) { 610 | this.serviceImpl = serviceImpl; 611 | this.methodId = methodId; 612 | } 613 | 614 | @java.lang.Override 615 | @java.lang.SuppressWarnings("unchecked") 616 | public void invoke(Req request, io.grpc.stub.StreamObserver responseObserver) { 617 | switch (methodId) { 618 | case METHODID_START_NATIVE: 619 | serviceImpl.startNative((muicore.MUICore.NativeArguments) request, 620 | (io.grpc.stub.StreamObserver) responseObserver); 621 | break; 622 | case METHODID_START_EVM: 623 | serviceImpl.startEVM((muicore.MUICore.EVMArguments) request, 624 | (io.grpc.stub.StreamObserver) responseObserver); 625 | break; 626 | case METHODID_TERMINATE: 627 | serviceImpl.terminate((muicore.MUICore.ManticoreInstance) request, 628 | (io.grpc.stub.StreamObserver) responseObserver); 629 | break; 630 | case METHODID_GET_STATE_LIST: 631 | serviceImpl.getStateList((muicore.MUICore.ManticoreInstance) request, 632 | (io.grpc.stub.StreamObserver) responseObserver); 633 | break; 634 | case METHODID_GET_MESSAGE_LIST: 635 | serviceImpl.getMessageList((muicore.MUICore.ManticoreInstance) request, 636 | (io.grpc.stub.StreamObserver) responseObserver); 637 | break; 638 | case METHODID_CHECK_MANTICORE_RUNNING: 639 | serviceImpl.checkManticoreRunning((muicore.MUICore.ManticoreInstance) request, 640 | (io.grpc.stub.StreamObserver) responseObserver); 641 | break; 642 | case METHODID_STOP_SERVER: 643 | serviceImpl.stopServer((muicore.MUICore.StopServerRequest) request, 644 | (io.grpc.stub.StreamObserver) responseObserver); 645 | break; 646 | default: 647 | throw new AssertionError(); 648 | } 649 | } 650 | 651 | @java.lang.Override 652 | @java.lang.SuppressWarnings("unchecked") 653 | public io.grpc.stub.StreamObserver invoke( 654 | io.grpc.stub.StreamObserver responseObserver) { 655 | switch (methodId) { 656 | default: 657 | throw new AssertionError(); 658 | } 659 | } 660 | } 661 | 662 | private static abstract class ManticoreUIBaseDescriptorSupplier 663 | implements io.grpc.protobuf.ProtoFileDescriptorSupplier, io.grpc.protobuf.ProtoServiceDescriptorSupplier { 664 | ManticoreUIBaseDescriptorSupplier() {} 665 | 666 | @java.lang.Override 667 | public com.google.protobuf.Descriptors.FileDescriptor getFileDescriptor() { 668 | return muicore.MUICore.getDescriptor(); 669 | } 670 | 671 | @java.lang.Override 672 | public com.google.protobuf.Descriptors.ServiceDescriptor getServiceDescriptor() { 673 | return getFileDescriptor().findServiceByName("ManticoreUI"); 674 | } 675 | } 676 | 677 | private static final class ManticoreUIFileDescriptorSupplier 678 | extends ManticoreUIBaseDescriptorSupplier { 679 | ManticoreUIFileDescriptorSupplier() {} 680 | } 681 | 682 | private static final class ManticoreUIMethodDescriptorSupplier 683 | extends ManticoreUIBaseDescriptorSupplier 684 | implements io.grpc.protobuf.ProtoMethodDescriptorSupplier { 685 | private final String methodName; 686 | 687 | ManticoreUIMethodDescriptorSupplier(String methodName) { 688 | this.methodName = methodName; 689 | } 690 | 691 | @java.lang.Override 692 | public com.google.protobuf.Descriptors.MethodDescriptor getMethodDescriptor() { 693 | return getServiceDescriptor().findMethodByName(methodName); 694 | } 695 | } 696 | 697 | private static volatile io.grpc.ServiceDescriptor serviceDescriptor; 698 | 699 | public static io.grpc.ServiceDescriptor getServiceDescriptor() { 700 | io.grpc.ServiceDescriptor result = serviceDescriptor; 701 | if (result == null) { 702 | synchronized (ManticoreUIGrpc.class) { 703 | result = serviceDescriptor; 704 | if (result == null) { 705 | serviceDescriptor = result = io.grpc.ServiceDescriptor.newBuilder(SERVICE_NAME) 706 | .setSchemaDescriptor(new ManticoreUIFileDescriptorSupplier()) 707 | .addMethod(getStartNativeMethod()) 708 | .addMethod(getStartEVMMethod()) 709 | .addMethod(getTerminateMethod()) 710 | .addMethod(getGetStateListMethod()) 711 | .addMethod(getGetMessageListMethod()) 712 | .addMethod(getCheckManticoreRunningMethod()) 713 | .addMethod(getStopServerMethod()) 714 | .build(); 715 | } 716 | } 717 | } 718 | return result; 719 | } 720 | } 721 | -------------------------------------------------------------------------------- /MUI/src/main/java/mui/MUIHookListComponent.java: -------------------------------------------------------------------------------- 1 | package mui; 2 | 3 | import java.awt.BorderLayout; 4 | import java.awt.Dimension; 5 | import java.awt.event.MouseListener; 6 | import java.awt.event.ActionEvent; 7 | import java.util.ArrayList; 8 | import java.util.HashMap; 9 | 10 | import javax.swing.AbstractAction; 11 | import javax.swing.JMenuItem; 12 | import javax.swing.JPanel; 13 | import javax.swing.JPopupMenu; 14 | import javax.swing.JScrollPane; 15 | import javax.swing.JTree; 16 | import javax.swing.SwingUtilities; 17 | import javax.swing.event.MouseInputAdapter; 18 | import javax.swing.tree.DefaultMutableTreeNode; 19 | import javax.swing.tree.DefaultTreeModel; 20 | import javax.swing.tree.TreePath; 21 | 22 | import muicore.MUICore.Hook; 23 | import muicore.MUICore.Hook.HookType; 24 | 25 | public class MUIHookListComponent extends JPanel { 26 | 27 | private JTree hookListTree; 28 | private JScrollPane hookListView; 29 | private DefaultTreeModel treeModel; 30 | 31 | private DefaultMutableTreeNode rootNode; 32 | private DefaultMutableTreeNode findNode; 33 | private DefaultMutableTreeNode avoidNode; 34 | private DefaultMutableTreeNode customNode; 35 | private DefaultMutableTreeNode globalNode; 36 | 37 | private HashMap hookLocations; 38 | 39 | private JPopupMenu hookListPopupMenu; 40 | private MUIHookUserObject rightClickedHook; 41 | 42 | public MUIHookListComponent() { 43 | setLayout(new BorderLayout()); 44 | hookLocations = new HashMap<>(); 45 | buildHookListView(); 46 | add(hookListView, BorderLayout.CENTER); 47 | setSize(new Dimension(900, 200)); 48 | setMaximumSize(new Dimension(900, 300)); 49 | setSize(new Dimension(900, 200)); 50 | setMaximumSize(new Dimension(900, 300)); 51 | 52 | } 53 | 54 | private void buildHookListView() { 55 | rootNode = new DefaultMutableTreeNode("Hooks"); 56 | findNode = new DefaultMutableTreeNode("Find"); 57 | avoidNode = new DefaultMutableTreeNode("Avoid"); 58 | customNode = new DefaultMutableTreeNode("Custom"); 59 | globalNode = new DefaultMutableTreeNode("Global"); 60 | 61 | rootNode.add(findNode); 62 | rootNode.add(avoidNode); 63 | rootNode.add(customNode); 64 | rootNode.add(globalNode); 65 | 66 | treeModel = new DefaultTreeModel(rootNode); 67 | 68 | hookListTree = new JTree(treeModel); 69 | hookListTree.setMinimumSize(new Dimension(0, 0)); 70 | hookListTree.setPreferredSize(new Dimension(900, 100)); 71 | hookListView = new JScrollPane(hookListTree); 72 | hookListView.setMinimumSize(new Dimension(0, 0)); 73 | hookListTree.setPreferredSize(new Dimension(900, 100)); 74 | 75 | hookListPopupMenu = new JPopupMenu(); 76 | 77 | hookListPopupMenu.add(new JMenuItem(new AbstractAction("Delete Hook") { 78 | 79 | @Override 80 | public void actionPerformed(ActionEvent e) { 81 | if (rightClickedHook != null) { 82 | switch (rightClickedHook.type) { 83 | case FIND: 84 | case AVOID: 85 | MUIPlugin.popup.unsetColor(rightClickedHook.address); 86 | case CUSTOM: 87 | case GLOBAL: 88 | removeHookIfExists(rightClickedHook.name, rightClickedHook.type); 89 | default: 90 | break; 91 | } 92 | } 93 | } 94 | 95 | })); 96 | 97 | hookListTree.addMouseListener(new MouseInputAdapter() { 98 | @Override 99 | public void mouseClicked(java.awt.event.MouseEvent e) { 100 | if (SwingUtilities.isRightMouseButton(e)) { 101 | TreePath path = hookListTree.getClosestPathForLocation(e.getX(), e.getY()); 102 | if (path != null) { 103 | DefaultMutableTreeNode node = 104 | (DefaultMutableTreeNode) path.getLastPathComponent(); 105 | if (node.getUserObject() instanceof MUIHookUserObject) { 106 | rightClickedHook = (MUIHookUserObject) node.getUserObject(); 107 | hookListPopupMenu.show(hookListTree, e.getX(), e.getY()); 108 | } 109 | } 110 | } 111 | } 112 | }); 113 | } 114 | 115 | public void addHook(MUIHookUserObject hook) { 116 | DefaultMutableTreeNode node = new DefaultMutableTreeNode(hook); 117 | switch (hook.type) { 118 | case FIND: 119 | findNode.add(node); 120 | break; 121 | case AVOID: 122 | avoidNode.add(node); 123 | break; 124 | case CUSTOM: 125 | customNode.add(node); 126 | break; 127 | case GLOBAL: 128 | globalNode.add(node); 129 | break; 130 | default: 131 | break; 132 | } 133 | 134 | hookLocations.put(hook.name.toString() + hook.type.name(), node); 135 | 136 | // TODO: Show hook counts? 137 | 138 | treeModel.reload(); 139 | expandTree(); 140 | 141 | } 142 | 143 | public boolean removeHookIfExists(String name, HookType type) { 144 | DefaultMutableTreeNode target = hookLocations.get(name + type.name()); 145 | 146 | if (target == null) { 147 | return false; 148 | } 149 | 150 | target.removeFromParent(); 151 | treeModel.reload(); 152 | expandTree(); 153 | return true; 154 | } 155 | 156 | public void clearHooks() { 157 | 158 | findNode.removeAllChildren(); 159 | avoidNode.removeAllChildren(); 160 | 161 | treeModel.reload(); 162 | 163 | } 164 | 165 | public ArrayList getAllMUIHooks() { 166 | ArrayList hooks = new ArrayList<>(); 167 | 168 | for (int i = 0; i < findNode.getChildCount(); i++) { 169 | hooks.add(nodeToMUIHook((DefaultMutableTreeNode) findNode.getChildAt(i))); 170 | } 171 | for (int i = 0; i < avoidNode.getChildCount(); i++) { 172 | hooks.add(nodeToMUIHook((DefaultMutableTreeNode) avoidNode.getChildAt(i))); 173 | } 174 | for (int i = 0; i < customNode.getChildCount(); i++) { 175 | hooks.add(nodeToMUIHook((DefaultMutableTreeNode) customNode.getChildAt(i))); 176 | } 177 | for (int i = 0; i < globalNode.getChildCount(); i++) { 178 | hooks.add(nodeToMUIHook((DefaultMutableTreeNode) globalNode.getChildAt(i))); 179 | } 180 | 181 | return hooks; 182 | } 183 | 184 | private void expandTree() { 185 | int row = 1; 186 | while (row++ < hookListTree.getRowCount()) { 187 | hookListTree.expandRow(row); 188 | } 189 | } 190 | 191 | private Hook nodeToMUIHook(DefaultMutableTreeNode node) { 192 | MUIHookUserObject hook = (MUIHookUserObject) node.getUserObject(); 193 | return hook.toMUIHook(); 194 | } 195 | } 196 | -------------------------------------------------------------------------------- /MUI/src/main/java/mui/MUIHookUserObject.java: -------------------------------------------------------------------------------- 1 | package mui; 2 | 3 | import java.time.ZoneId; 4 | import java.time.ZonedDateTime; 5 | import java.time.format.DateTimeFormatter; 6 | 7 | import ghidra.program.model.address.Address; 8 | import muicore.MUICore.Hook; 9 | import muicore.MUICore.Hook.HookType; 10 | import muicore.MUICore.Hook.Builder; 11 | 12 | public class MUIHookUserObject { 13 | public HookType type; 14 | public String name; 15 | public Address address; 16 | public String func_text; 17 | 18 | public MUIHookUserObject(HookType type, Address address, String func_text) { 19 | this.type = type; 20 | this.name = address.toString(); 21 | this.address = address; 22 | this.func_text = func_text; 23 | } 24 | 25 | public MUIHookUserObject(HookType type, String func_text) { 26 | this.type = type; 27 | this.name = "Global " + ZonedDateTime.now(ZoneId.systemDefault()) 28 | .format(DateTimeFormatter.ofPattern("HH:mm:ss")); 29 | this.func_text = func_text; 30 | } 31 | 32 | public Hook toMUIHook() { 33 | Builder b = Hook.newBuilder().setType(type); 34 | switch (type) { 35 | case FIND: 36 | case AVOID: 37 | b.setAddress( 38 | Long.parseLong(address.toString(), 16)); 39 | break; 40 | case CUSTOM: 41 | b.setAddress( 42 | Long.parseLong(address.toString(), 16)); 43 | case GLOBAL: 44 | b.setFuncText(func_text); 45 | break; 46 | default: 47 | break; 48 | } 49 | return b.build(); 50 | 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /MUI/src/main/java/mui/MUILogContentComponent.java: -------------------------------------------------------------------------------- 1 | package mui; 2 | 3 | import java.awt.*; 4 | import java.awt.event.*; 5 | import javax.swing.Box; 6 | import javax.swing.JButton; 7 | import javax.swing.JPanel; 8 | import javax.swing.JScrollPane; 9 | import javax.swing.JTextArea; 10 | import javax.swing.JToggleButton; 11 | import javax.swing.JToolBar; 12 | import javax.swing.ScrollPaneConstants; 13 | import resources.ResourceManager; 14 | 15 | /** 16 | * Provides the component for the Tab Content in the MUI Log tabbed pane. 17 | */ 18 | public class MUILogContentComponent extends JPanel { 19 | 20 | public ManticoreRunner manticoreRunner; 21 | 22 | public JTextArea logArea; 23 | public JButton stopButton; 24 | public JToggleButton scrollLockButton; 25 | 26 | public MUILogContentComponent(ManticoreRunner mcore) { 27 | 28 | manticoreRunner = mcore; 29 | 30 | setLayout(new BorderLayout()); 31 | setMinimumSize(new Dimension(300, 300)); 32 | 33 | logArea = new JTextArea(); 34 | stopButton = new JButton(); 35 | scrollLockButton = new JToggleButton(); 36 | 37 | buildLogArea(); 38 | buildToolBar(); 39 | } 40 | 41 | /** 42 | * Builds a scrollable, uneditable TextArea which displays the logs of a Manticore instance. 43 | */ 44 | public void buildLogArea() { 45 | logArea.setEditable(false); 46 | logArea.setLineWrap(true); 47 | logArea.setWrapStyleWord(true); 48 | JScrollPane logScrollPane = 49 | new JScrollPane( 50 | logArea, 51 | ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED, 52 | ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED); 53 | 54 | add(logScrollPane, BorderLayout.CENTER); 55 | } 56 | 57 | /** 58 | * Builds the log's toolbar including a Stop button that will terminate the Manticore instance of the currently-focused tab. 59 | */ 60 | public void buildToolBar() { 61 | JToolBar logToolBar = new JToolBar(); 62 | logToolBar.setFloatable(false); 63 | stopButton.setEnabled(true); 64 | stopButton.setIcon(ResourceManager.loadImage("images/stopNode.png")); 65 | stopButton.addActionListener( 66 | new ActionListener() { 67 | 68 | @Override 69 | public void actionPerformed(ActionEvent e) { 70 | manticoreRunner.terminateManticore(); 71 | } 72 | 73 | }); 74 | stopButton.setToolTipText("Terminate Execution"); 75 | 76 | scrollLockButton.setEnabled(true); 77 | scrollLockButton.setIcon(ResourceManager.loadImage("images/lock.png")); 78 | scrollLockButton.setToolTipText("Toggle Auto-Scroll Lock"); 79 | 80 | logToolBar.add(Box.createGlue()); // shifts buttons to the right 81 | logToolBar.add(stopButton); 82 | logToolBar.add(scrollLockButton); 83 | 84 | add(logToolBar, BorderLayout.PAGE_START); 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /MUI/src/main/java/mui/MUILogProvider.java: -------------------------------------------------------------------------------- 1 | package mui; 2 | 3 | import docking.*; 4 | import ghidra.framework.plugintool.ComponentProviderAdapter; 5 | import ghidra.framework.plugintool.PluginTool; 6 | 7 | import java.awt.*; 8 | import java.time.Instant; 9 | import java.time.ZoneId; 10 | import java.time.ZonedDateTime; 11 | import java.time.format.DateTimeFormatter; 12 | 13 | import javax.swing.*; 14 | 15 | /** 16 | * Provides the "MUI Log" component used to display Manticore Logs. Also acts as the control center for the StateList component and for managing the different Manticore instances. 17 | */ 18 | public class MUILogProvider extends ComponentProviderAdapter { 19 | 20 | private JPanel logPanel; 21 | private JTabbedPane logTabPane; 22 | 23 | public MUILogProvider(PluginTool tool, String name) { 24 | super(tool, name, name); 25 | buildLogPanel(); 26 | setTitle("MUI Log"); 27 | setDefaultWindowPosition(WindowPosition.BOTTOM); 28 | setVisible(false); 29 | } 30 | 31 | /** 32 | * Builds main component panel which hosts multiple log tabs. 33 | */ 34 | private void buildLogPanel() { 35 | logPanel = new JPanel(); 36 | logPanel.setLayout(new BorderLayout()); 37 | logPanel.setMinimumSize(new Dimension(500, 300)); 38 | 39 | logTabPane = new JTabbedPane(); 40 | logPanel.add(logTabPane); 41 | } 42 | 43 | /** 44 | * Builds and makes changes to UI elements when a user attempts to run a new instance of Manticore, and calls the function that actually creates the new Manticore process. 45 | * @param programPath Path of the binary being analyzed. 46 | * @param formOptions Map storing pre-selected key Manticore options. 47 | * @param moreArgs Additional Manticore arguments set by the user. 48 | */ 49 | public void addLogTab(ManticoreRunner manticoreRunner) { 50 | 51 | MUILogContentComponent newTabContent = new MUILogContentComponent(manticoreRunner); 52 | manticoreRunner.setLogUIElems(newTabContent); 53 | 54 | logTabPane.add( 55 | ZonedDateTime.now(ZoneId.systemDefault()) 56 | .format(DateTimeFormatter.ofPattern("HH:mm:ss")), 57 | newTabContent); 58 | logTabPane.setTabComponentAt( 59 | logTabPane.getTabCount() - 1, new MUILogTabComponent(logTabPane, this)); 60 | logTabPane.setSelectedIndex(logTabPane.getTabCount() - 1); 61 | newTabContent.requestFocusInWindow(); 62 | 63 | fetchLogs(manticoreRunner); 64 | MUIStateListProvider.changeRunner(manticoreRunner); 65 | 66 | } 67 | 68 | private void fetchLogs(ManticoreRunner manticoreRunner) { 69 | 70 | SwingWorker sw = new SwingWorker() { 71 | 72 | @Override 73 | protected Object doInBackground() throws Exception { 74 | manticoreRunner.getLogText() 75 | .append("Manticore process started..." + System.lineSeparator()); 76 | boolean waiting = true; 77 | long prevTime = Instant.now().getEpochSecond(); 78 | while (waiting) { 79 | if (Instant.now().getEpochSecond() - 1 > prevTime) { 80 | prevTime = Instant.now().getEpochSecond(); 81 | waiting = !manticoreRunner.getHasStarted(); 82 | 83 | } 84 | } // wait until started 85 | 86 | prevTime = Instant.now().getEpochSecond(); 87 | while (manticoreRunner.getIsRunning()) { 88 | if (Instant.now().getEpochSecond() - 1 > prevTime) { 89 | manticoreRunner.fetchMessageLogs(); 90 | manticoreRunner.fetchStateList(); 91 | manticoreRunner.fetchIsRunning(); 92 | prevTime = Instant.now().getEpochSecond(); 93 | } 94 | } 95 | return null; 96 | } 97 | 98 | @Override 99 | protected void done() { 100 | manticoreRunner.getStopBtn().setEnabled(false); 101 | manticoreRunner.fetchMessageLogs(); 102 | manticoreRunner.fetchStateList(); 103 | if (manticoreRunner.getWasTerminated()) { 104 | manticoreRunner.getLogText() 105 | .append( 106 | System.lineSeparator() + "Manticore process terminated by user."); 107 | } 108 | else { 109 | manticoreRunner.getLogText() 110 | .append(System.lineSeparator() + "Manticore process completed."); 111 | } 112 | } 113 | 114 | }; 115 | 116 | sw.execute(); 117 | } 118 | 119 | /** 120 | * Performs auxiliary actions when closing a tab, including stopping the Manticore instance and removing the tab component from the tab pane. 121 | * @param tabIndex The index of the closed tab in the MUI Log tab pane. 122 | */ 123 | public void closeLogTab(int tabIndex) { 124 | MUILogContentComponent curComp = 125 | (MUILogContentComponent) logTabPane.getComponentAt(tabIndex); 126 | if (curComp.manticoreRunner.getIsRunning()) { 127 | curComp.manticoreRunner.terminateManticore(); 128 | } 129 | logTabPane.remove(tabIndex); 130 | } 131 | 132 | @Override 133 | public JComponent getComponent() { 134 | return logPanel; 135 | } 136 | } 137 | -------------------------------------------------------------------------------- /MUI/src/main/java/mui/MUILogTabComponent.java: -------------------------------------------------------------------------------- 1 | package mui; 2 | 3 | import java.awt.*; 4 | import java.awt.event.*; 5 | import javax.swing.*; 6 | import javax.swing.plaf.basic.BasicButtonUI; 7 | 8 | /** 9 | * Provides the component for the Tab itself in the MUI Log tabbed pane, including behaviour when switching and closing tabs. 10 | */ 11 | public class MUILogTabComponent extends JPanel { 12 | private JTabbedPane parentPane; 13 | private MUILogProvider logProvider; 14 | 15 | public MUILogTabComponent(JTabbedPane parentPane, MUILogProvider logProvider) { 16 | super(new FlowLayout(FlowLayout.LEFT, 0, 0)); 17 | if (parentPane == null) { 18 | throw new NullPointerException("no parent pane!"); 19 | } 20 | this.parentPane = parentPane; 21 | this.logProvider = logProvider; 22 | setOpaque(false); 23 | 24 | JLabel titleLbl = 25 | new JLabel() { 26 | public String getText() { 27 | int i = parentPane.indexOfTabComponent(MUILogTabComponent.this); 28 | if (i != -1) { 29 | return parentPane.getTitleAt(i); 30 | } 31 | return null; 32 | } 33 | }; 34 | 35 | add(titleLbl); 36 | titleLbl.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 5)); 37 | titleLbl.addMouseListener(switchTabMouseAdapter); 38 | JButton button = new LogTabCloseButton(); 39 | add(button); 40 | setBorder(BorderFactory.createEmptyBorder(2, 0, 0, 0)); 41 | addMouseListener(switchTabMouseAdapter); 42 | } 43 | 44 | private class LogTabCloseButton extends JButton implements ActionListener { 45 | public LogTabCloseButton() { 46 | int size = 17; 47 | setPreferredSize(new Dimension(size, size)); 48 | setToolTipText("close this tab"); 49 | setUI(new BasicButtonUI()); 50 | setContentAreaFilled(false); 51 | setFocusable(false); 52 | setBorder(BorderFactory.createEtchedBorder()); 53 | setBorderPainted(false); 54 | addMouseListener(closeButtonMouseListener); 55 | setRolloverEnabled(true); 56 | addActionListener(this); 57 | } 58 | 59 | /** 60 | * Closes tab when close button is clicked, and calls additional method to handle auxiliary functions. 61 | * @see MUILogProvider#closeLogTab(int) 62 | */ 63 | public void actionPerformed(ActionEvent e) { 64 | int i = parentPane.indexOfTabComponent(MUILogTabComponent.this); 65 | if (i != -1) { 66 | logProvider.closeLogTab(i); 67 | } 68 | } 69 | 70 | /** 71 | * Overrides the updateUI method with no action, since the Tab and thus the Button will be closed on click anyways. 72 | */ 73 | @Override 74 | public void updateUI() { 75 | } 76 | 77 | /** 78 | * Natively paints the close button graphic. 79 | */ 80 | protected void paintComponent(Graphics g) { 81 | super.paintComponent(g); 82 | Graphics2D g2 = (Graphics2D) g.create(); 83 | if (getModel().isPressed()) { 84 | g2.translate(1, 1); 85 | } 86 | g2.setStroke(new BasicStroke(2)); 87 | g2.setColor(Color.BLACK); 88 | if (getModel().isRollover()) { 89 | g2.setColor(Color.MAGENTA); 90 | } 91 | int delta = 6; 92 | g2.drawLine(delta, delta, getWidth() - delta - 1, getHeight() - delta - 1); 93 | g2.drawLine(getWidth() - delta - 1, delta, delta, getHeight() - delta - 1); 94 | g2.dispose(); 95 | } 96 | } 97 | 98 | /** 99 | * Handles tab switching, including auxiliary behavior such as reflecting the State List of the newly-focused Manticore instance. 100 | */ 101 | private final MouseAdapter switchTabMouseAdapter = 102 | new MouseAdapter() { 103 | @Override 104 | public void mouseClicked(MouseEvent e) { 105 | int index = parentPane.indexOfTabComponent(MUILogTabComponent.this); 106 | parentPane.setSelectedIndex(index); 107 | MUILogContentComponent tabContent = 108 | (MUILogContentComponent) parentPane.getComponentAt(index); 109 | MUIStateListProvider.changeRunner(tabContent.manticoreRunner); 110 | } 111 | }; 112 | 113 | /** 114 | * Handles UI changes when mousing over and clicking into the close tab button. 115 | */ 116 | private final MouseListener closeButtonMouseListener = 117 | new MouseAdapter() { 118 | public void mouseEntered(MouseEvent e) { 119 | Component component = e.getComponent(); 120 | if (component instanceof AbstractButton) { 121 | AbstractButton button = (AbstractButton) component; 122 | button.setBorderPainted(true); 123 | } 124 | } 125 | 126 | public void mouseExited(MouseEvent e) { 127 | Component component = e.getComponent(); 128 | if (component instanceof AbstractButton) { 129 | AbstractButton button = (AbstractButton) component; 130 | button.setBorderPainted(false); 131 | } 132 | } 133 | }; 134 | } 135 | -------------------------------------------------------------------------------- /MUI/src/main/java/mui/MUIPlugin.java: -------------------------------------------------------------------------------- 1 | /* ### 2 | * IP: GHIDRA 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package mui; 17 | 18 | import javax.swing.JOptionPane; 19 | import javax.swing.JScrollPane; 20 | import javax.swing.JTextArea; 21 | import javax.swing.SwingWorker; 22 | 23 | import docking.ActionContext; 24 | import docking.action.DockingAction; 25 | import docking.action.MenuData; 26 | import ghidra.GhidraApplicationLayout; 27 | import ghidra.app.plugin.PluginCategoryNames; 28 | import ghidra.app.plugin.ProgramPlugin; 29 | import ghidra.framework.Application; 30 | import ghidra.framework.ApplicationConfiguration; 31 | import ghidra.framework.plugintool.*; 32 | import ghidra.framework.plugintool.util.PluginStatus; 33 | import ghidra.program.model.listing.Program; 34 | import ghidra.util.Msg; 35 | import io.grpc.ManagedChannel; 36 | import io.grpc.ManagedChannelBuilder; 37 | import muicore.ManticoreUIGrpc; 38 | import muicore.ManticoreUIGrpc.ManticoreUIBlockingStub; 39 | import muicore.ManticoreUIGrpc.ManticoreUIStub; 40 | import muicore.MUICore.StopServerRequest; 41 | import muicore.MUICore.Hook.HookType; 42 | 43 | // @formatter:off 44 | @PluginInfo( 45 | status = PluginStatus.UNSTABLE, 46 | packageName = "MUI", 47 | category = PluginCategoryNames.ANALYSIS, 48 | shortDescription = "Manticore User Interface", 49 | description = 50 | "GUI Plugin that allows users to easily interact with and view progress of the Manticore symbolic execution engine.") 51 | // @formatter:on 52 | public class MUIPlugin extends ProgramPlugin { 53 | 54 | public static MUISetupProvider setup; 55 | public static MUILogProvider log; 56 | public static MUIPopupMenu popup; 57 | public static MUIStateListProvider stateList; 58 | 59 | private String MUICoreServerPath; 60 | public static ManticoreUIBlockingStub blockingMUICoreStub; 61 | public static ManticoreUIStub asyncMUICoreStub; 62 | 63 | private DockingAction showSetup; 64 | private DockingAction showLog; 65 | private DockingAction showStateList; 66 | private DockingAction showCreateGlobalHookDialog; 67 | 68 | /** 69 | * The main extension constructor, initializes the plugin's components and sets up the "MUI" MenuBar tab. 70 | * @throws Exception 71 | */ 72 | public MUIPlugin(PluginTool tool) throws Exception { 73 | super(tool, true, true); 74 | 75 | startMUICoreServer(); 76 | initMUICoreStubs(); 77 | 78 | String pluginName = getName(); 79 | log = new MUILogProvider(tool, pluginName); 80 | popup = new MUIPopupMenu(tool, pluginName); 81 | stateList = new MUIStateListProvider(tool, pluginName); 82 | setup = new MUISetupProvider(tool, pluginName); 83 | 84 | showSetup = new DockingAction("Run Manticore", pluginName) { 85 | 86 | @Override 87 | public void actionPerformed(ActionContext context) { 88 | setup.setVisible(true); 89 | } 90 | }; 91 | 92 | showLog = new DockingAction("Show Log", pluginName) { 93 | 94 | @Override 95 | public void actionPerformed(ActionContext context) { 96 | log.setVisible(true); 97 | } 98 | }; 99 | 100 | showStateList = new DockingAction("Show State List", pluginName) { 101 | 102 | @Override 103 | public void actionPerformed(ActionContext context) { 104 | stateList.setVisible(true); 105 | } 106 | 107 | }; 108 | 109 | showCreateGlobalHookDialog = new DockingAction("Create Global Hook", pluginName) { 110 | 111 | @Override 112 | public void actionPerformed(ActionContext context) { 113 | JTextArea textArea = 114 | new JTextArea("global m\ndef hook(state):\n pass\nm.hook(None)(hook)"); 115 | JScrollPane scrollPane = new JScrollPane(textArea); 116 | int result = JOptionPane.showConfirmDialog(null, scrollPane, "Create Global Hook", 117 | JOptionPane.OK_CANCEL_OPTION, JOptionPane.PLAIN_MESSAGE); 118 | if (result == JOptionPane.OK_OPTION) { 119 | String func_text = textArea.getText(); 120 | setup.setupHookList 121 | .addHook(new MUIHookUserObject(HookType.GLOBAL, func_text)); 122 | } 123 | } 124 | 125 | }; 126 | 127 | showSetup.setMenuBarData(new MenuData(new String[] { "MUI", "Run Manticore" })); 128 | showLog.setMenuBarData(new MenuData(new String[] { "MUI", "Show Log" })); 129 | showStateList.setMenuBarData(new MenuData(new String[] { "MUI", "Show State List" })); 130 | showCreateGlobalHookDialog 131 | .setMenuBarData(new MenuData(new String[] { "MUI", "Create Global Hook" })); 132 | 133 | tool.addAction(showSetup); 134 | tool.addAction(showLog); 135 | tool.addAction(showStateList); 136 | tool.addAction(showCreateGlobalHookDialog); 137 | } 138 | 139 | /** 140 | * Starts the MUICore server using the muicore_server binary included in the extension. 141 | * 142 | * Should eventually be optimized such that it's created only when needed, and automatically 143 | * destroys after a set period of inactivity. 144 | * @throws Exception 145 | */ 146 | 147 | public void startMUICoreServer() throws Exception { 148 | try { 149 | if (!Application.isInitialized()) { 150 | Application.initializeApplication( 151 | new GhidraApplicationLayout(), new ApplicationConfiguration()); 152 | } 153 | MUICoreServerPath = Application.getOSFile("muicore_server").getCanonicalPath(); 154 | } 155 | catch (Exception e) { 156 | throw e; 157 | } 158 | 159 | SwingWorker sw = 160 | new SwingWorker() { 161 | 162 | @Override 163 | protected Object doInBackground() throws Exception { 164 | ProcessBuilder pb = new ProcessBuilder(MUICoreServerPath); 165 | Process p = pb.start(); 166 | Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() { 167 | 168 | @Override 169 | public void run() { 170 | blockingMUICoreStub.stopServer(StopServerRequest.newBuilder().build()); 171 | } 172 | 173 | })); 174 | return null; 175 | } 176 | }; 177 | sw.execute(); 178 | } 179 | 180 | /** 181 | * Initializes the gRPC Stub classes that allows the plugin to communicate with the MUICore server as a client. 182 | */ 183 | public void initMUICoreStubs() { 184 | ManagedChannel channel = 185 | ManagedChannelBuilder.forTarget("localhost:50010").usePlaintext().build(); 186 | blockingMUICoreStub = ManticoreUIGrpc.newBlockingStub(channel); 187 | asyncMUICoreStub = ManticoreUIGrpc.newStub(channel); 188 | } 189 | 190 | @Override 191 | protected void programActivated(Program p) { 192 | setup.setProgram(p); 193 | popup.setProgram(p); 194 | } 195 | } 196 | -------------------------------------------------------------------------------- /MUI/src/main/java/mui/MUIPopupMenu.java: -------------------------------------------------------------------------------- 1 | package mui; 2 | 3 | import java.awt.Color; 4 | import java.util.HashSet; 5 | 6 | import javax.swing.JOptionPane; 7 | import javax.swing.JScrollPane; 8 | import javax.swing.JTextArea; 9 | 10 | import docking.action.MenuData; 11 | import ghidra.app.context.ListingActionContext; 12 | import ghidra.app.context.ListingContextAction; 13 | import ghidra.app.plugin.core.colorizer.ColorizingService; 14 | import ghidra.framework.plugintool.PluginTool; 15 | import ghidra.program.model.listing.Program; 16 | import muicore.MUICore.Hook.HookType; 17 | import ghidra.program.model.address.Address; 18 | 19 | /** 20 | * Provides functionality to Find/Avoid a specific address via a right-click context menu item in the Listing component. 21 | */ 22 | public class MUIPopupMenu extends ListingContextAction { 23 | 24 | private static Program program; 25 | public static PluginTool pluginTool; 26 | 27 | public MUIPopupMenu(PluginTool tool, String name) { 28 | super(name, name); 29 | pluginTool = tool; 30 | setupMenu(); 31 | } 32 | 33 | /** 34 | * Clears color from the Listing component for the specified address. 35 | * @param address The address to clear color from. 36 | */ 37 | public static void unsetColor(Address address) { 38 | ColorizingService service = pluginTool.getService(ColorizingService.class); 39 | int tid = program.startTransaction("unsetColor"); 40 | service.clearBackgroundColor(address, address); 41 | program.endTransaction(tid, true); 42 | } 43 | 44 | /** 45 | * Sets color in the Listing component for the specified address. 46 | * @param address The address to clear color from. 47 | * @param color The color to set for the address in the Listing component. 48 | */ 49 | public static void setColor(Address address, Color color) { 50 | ColorizingService service = pluginTool.getService(ColorizingService.class); 51 | int tid = program.startTransaction("setColor"); 52 | service.setBackgroundColor(address, address, color); 53 | program.endTransaction(tid, true); 54 | 55 | } 56 | 57 | /** 58 | * Builds the menu items in the right-click context menu of the Listing component. 59 | */ 60 | public void setupMenu() { 61 | pluginTool.setMenuGroup(new String[] { "MUI" }, "MUI"); 62 | 63 | ListingContextAction findInstruction = 64 | new ListingContextAction("Toggle Find Instruction", "MUI") { 65 | @Override 66 | protected void actionPerformed(ListingActionContext context) { 67 | Address selectedAddr = context.getLocation().getAddress(); 68 | 69 | MUIPlugin.setup.setupHookList.removeHookIfExists(selectedAddr.toString(), 70 | HookType.AVOID); 71 | 72 | if (MUIPlugin.setup.setupHookList.removeHookIfExists(selectedAddr.toString(), 73 | HookType.FIND)) { 74 | unsetColor(selectedAddr); 75 | } 76 | else { 77 | MUIPlugin.setup.setupHookList.addHook( 78 | new MUIHookUserObject(HookType.FIND, selectedAddr, null)); 79 | setColor(selectedAddr, Color.GREEN); 80 | } 81 | 82 | } 83 | }; 84 | 85 | findInstruction.setPopupMenuData(new MenuData(new String[] { 86 | "MUI", 87 | "Toggle Find Instruction", 88 | })); 89 | 90 | pluginTool.addAction(findInstruction); 91 | 92 | ListingContextAction avoidInstruction = 93 | new ListingContextAction("Toggle Avoid Instruction", "MUI") { 94 | @Override 95 | protected void actionPerformed(ListingActionContext context) { 96 | Address selectedAddr = context.getLocation().getAddress(); 97 | 98 | MUIPlugin.setup.setupHookList.removeHookIfExists(selectedAddr.toString(), 99 | HookType.FIND); 100 | 101 | if (MUIPlugin.setup.setupHookList.removeHookIfExists(selectedAddr.toString(), 102 | HookType.AVOID)) { 103 | unsetColor(selectedAddr); 104 | } 105 | else { 106 | MUIPlugin.setup.setupHookList.addHook( 107 | new MUIHookUserObject(HookType.AVOID, selectedAddr, null)); 108 | setColor(selectedAddr, Color.RED); 109 | } 110 | 111 | } 112 | }; 113 | 114 | avoidInstruction.setPopupMenuData(new MenuData(new String[] { 115 | "MUI", 116 | "Toggle Avoid Instruction", 117 | })); 118 | 119 | pluginTool.addAction(avoidInstruction); 120 | 121 | ListingContextAction addCustomHookAtInstruction = 122 | new ListingContextAction("Add Custom Hook at Instruction", "MUI") { 123 | @Override 124 | protected void actionPerformed(ListingActionContext context) { 125 | Address selectedAddr = context.getLocation().getAddress(); 126 | JTextArea textArea = new JTextArea( 127 | "global m, addr\ndef hook(state):\n pass\nm.hook(addr)(hook)"); 128 | JScrollPane scrollPane = new JScrollPane(textArea); 129 | int result = JOptionPane.showConfirmDialog(null, scrollPane, 130 | "Create Custom Hook at " + selectedAddr.toString(), 131 | JOptionPane.OK_CANCEL_OPTION, JOptionPane.PLAIN_MESSAGE); 132 | if (result == JOptionPane.OK_OPTION) { 133 | String func_text = textArea.getText(); 134 | MUIPlugin.setup.setupHookList.addHook(new MUIHookUserObject(HookType.CUSTOM, 135 | selectedAddr, func_text)); 136 | } 137 | } 138 | }; 139 | addCustomHookAtInstruction.setPopupMenuData(new MenuData(new String[] { 140 | "MUI", 141 | "Add Custom Hook at Instruction", 142 | })); 143 | 144 | pluginTool.addAction(addCustomHookAtInstruction); 145 | } 146 | 147 | /** 148 | * Called once the binary being analyzed in Ghidra has been activated. 149 | * @param p the binary being analyzed in Ghidra 150 | * @see MUIPlugin#programActivated(Program) 151 | */ 152 | public void setProgram(Program p) { 153 | program = p; 154 | } 155 | 156 | } 157 | -------------------------------------------------------------------------------- /MUI/src/main/java/mui/MUISettings.java: -------------------------------------------------------------------------------- 1 | package mui; 2 | 3 | import java.util.*; 4 | 5 | /** 6 | * Outlines the key manticore arguments to display for the user, as well as their sensible defaults. Should eventually be deprecated for a more interoperable format. 7 | * @see Binary Ninja plugin equivalent 8 | */ 9 | public class MUISettings { 10 | 11 | /** 12 | * Map containing key Manticore arguments and their details including input types, defaults, and descriptions. 13 | */ 14 | public static Map[]>> SETTINGS = 15 | Map.of( 16 | "NATIVE_RUN_SETTINGS", 17 | new TreeMap(Map.of( 18 | "data", new Map[] { 19 | Map.of( 20 | "title", "Concrete Start", 21 | "description", "Initial concrete data for the input symbolic buffer", 22 | "type", "string", 23 | "default", ""), 24 | Map.of() }, 25 | "native.stdin_size", new Map[] { 26 | Map.of( 27 | "title", "Stdin Size", 28 | "description", "Stdin size to use for manticore", 29 | "type", "number", 30 | "default", 256), 31 | Map.of() }, 32 | "argv", new Map[] { 33 | Map.of( 34 | "title", "Program arguments (use + as a wildcard)", 35 | "description", "Argv to use for manticore", 36 | "type", "array", 37 | "elementType", "string", 38 | "default", ""), 39 | Map.of() }, 40 | "env", new Map[] { 41 | Map.of( 42 | "title", "Environment Variables", 43 | "description", "Environment variables for manticore", 44 | "type", "array", 45 | "elementType", "string", 46 | "default", ""), 47 | Map.of() }, 48 | "file", new Map[] { 49 | Map.of( 50 | "title", "Symbolic Input Files", 51 | "description", "Symbolic input files for manticore", 52 | "type", "array", 53 | "elementType", "string", 54 | "default", ""), 55 | Map.of() }))); 56 | 57 | } 58 | -------------------------------------------------------------------------------- /MUI/src/main/java/mui/MUISetupProvider.java: -------------------------------------------------------------------------------- 1 | package mui; 2 | 3 | import docking.*; 4 | import ghidra.framework.plugintool.ComponentProviderAdapter; 5 | import ghidra.framework.plugintool.PluginTool; 6 | import ghidra.program.model.listing.Program; 7 | import muicore.MUICore.Hook; 8 | import muicore.MUICore.NativeArguments; 9 | import muicore.MUICore.Hook.HookType; 10 | 11 | import java.awt.*; 12 | import java.awt.event.*; 13 | import java.io.IOException; 14 | import java.util.Arrays; 15 | import java.util.HashMap; 16 | import java.util.List; 17 | import java.util.Map; 18 | import java.util.Map.Entry; 19 | import java.util.ArrayList; 20 | import javax.swing.*; 21 | 22 | /** 23 | * Provides the "MUI Setup" component used to set the arguments for and run Manticore. 24 | */ 25 | public class MUISetupProvider extends ComponentProviderAdapter { 26 | 27 | private Program program; 28 | 29 | private JPanel mainPanel; 30 | private String programPath; 31 | private JPanel formPanel; 32 | 33 | public MUIHookListComponent setupHookList; 34 | 35 | private HashMap formOptions; 36 | 37 | public MUISetupProvider(PluginTool tool, String name) { 38 | super(tool, name, name); 39 | buildFormPanel(); 40 | buildMainPanel(); 41 | setTitle("MUI Setup"); 42 | setDefaultWindowPosition(WindowPosition.WINDOW); 43 | setVisible(false); 44 | } 45 | 46 | /** 47 | * Builds the component where key manticore arguments are displayed with dedicated input fields which are loaded with sensible defaults. Arguments include are defined in MUISettings. 48 | * @see MUISettings#SETTINGS 49 | * @throws UnsupportedOperationException 50 | */ 51 | private void buildFormPanel() throws UnsupportedOperationException { 52 | formPanel = new JPanel(); 53 | formPanel.setLayout( 54 | new GridLayout(MUISettings.SETTINGS.get("NATIVE_RUN_SETTINGS").size(), 2)); 55 | formPanel.setMinimumSize(new Dimension(800, 500)); 56 | 57 | formOptions = new HashMap(); 58 | 59 | for (Entry[]> option : MUISettings.SETTINGS 60 | .get("NATIVE_RUN_SETTINGS") 61 | .entrySet()) { 62 | String name = option.getKey(); 63 | 64 | Map prop = option.getValue()[0]; 65 | Map extra = option.getValue()[1]; 66 | 67 | String title = (String) prop.get("title"); 68 | formPanel.add(new JLabel(title)); 69 | 70 | if (extra.containsKey("is_dir_path") && (Boolean) extra.get("is_dir_path")) { 71 | formOptions.put(name, 72 | createPathInput(prop.get("default").toString())); 73 | } 74 | else if (prop.get("type") == "string" || prop.get("type") == "number") { 75 | formOptions.put(name, createStringNumberInput(prop.get("default").toString())); 76 | } 77 | else if (prop.get("type") == "array") { 78 | formOptions.put(name, createArrayInput()); 79 | } 80 | else { 81 | // TODO: to achieve parity with Binja MUI, type==boolean must be supported, but not needed as part of sensible defaults for running manticore on native binaries 82 | throw new UnsupportedOperationException( 83 | String.format("[ERROR] Cannot create input row for %s with the type %s", name, 84 | prop.get("type"))); 85 | } 86 | } 87 | } 88 | 89 | /** 90 | * Creates JTextField for a string/number Manticore argument and adds it to the Setup Panel. 91 | * @param defaultStr The default value of the string/number argument, which should be set in MUISettings. 92 | * @return An editable JTextField through which a user can set a string/number argument. 93 | */ 94 | private JTextField createStringNumberInput(String defaultStr) { 95 | JTextField entry = new JTextField(); 96 | entry.setText(defaultStr); 97 | entry.setToolTipText("Only 1 value allowed"); 98 | formPanel.add(entry); 99 | return entry; 100 | } 101 | 102 | /** 103 | * Creates JTextField for an array Manticore argument and adds it to the Setup Panel. The user is expected to space-separate each element in the array. Tokenizer supports escaped spaces or spaces within quotes. 104 | * @implNote Should mimic Binary Ninja plugin behavior in future, which would allow for removal of tokenizer. 105 | * @see MUILogProvider#tokenizeArrayInput 106 | * @return An editable JTextField through which a user can set an array argument. 107 | */ 108 | private JTextField createArrayInput() { 109 | // TODO: doesn't handle default param for arrays, but not needed as part of sensible defaults for running manticore on native binaries 110 | // for now, same UI as string/num, and we will parse space-separated args 111 | JTextField entry = new JTextField(); 112 | entry.setToolTipText("You can space-separate multiple arguments"); 113 | formPanel.add(entry); 114 | return entry; 115 | } 116 | 117 | /** 118 | * Creates JTextField and file selector dialog (shown by a select button) for a path Manticore argument and adds it to the Setup Panel. 119 | * @param defaultStr The default value of the path argument. 120 | * @return An editable JTextField through which a user can set a path argument. 121 | */ 122 | private JTextField createPathInput(String defaultStr) { 123 | JTextField entry = new JTextField(); 124 | 125 | JPanel inputRow = new JPanel(new GridBagLayout()); 126 | GridBagConstraints constraints = new GridBagConstraints(); 127 | constraints.fill = GridBagConstraints.HORIZONTAL; 128 | //inputRow.setMinimumSize(new Dimension(800, 100)); 129 | 130 | entry.setText(defaultStr); 131 | entry.setPreferredSize(new Dimension(160, 27)); 132 | constraints.gridx = 0; 133 | constraints.gridwidth = 2; 134 | constraints.gridy = 0; 135 | constraints.weightx = 0.66; 136 | inputRow.add(entry, constraints); 137 | 138 | JFileChooser chooser = new JFileChooser(); 139 | chooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY); 140 | chooser.setDialogTitle("Set Workspace Folder"); 141 | 142 | JButton selectButton = new JButton("Select..."); 143 | selectButton.addActionListener(new ActionListener() { 144 | 145 | @Override 146 | public void actionPerformed(ActionEvent e) { 147 | int returnVal = chooser.showOpenDialog(null); 148 | if (returnVal == JFileChooser.APPROVE_OPTION) { 149 | try { 150 | String path = chooser.getSelectedFile().getCanonicalPath(); 151 | entry.setText(path); 152 | } 153 | catch (IOException e1) { 154 | e1.printStackTrace(); 155 | } 156 | } 157 | } 158 | 159 | }); 160 | constraints.gridx = 2; 161 | constraints.gridwidth = 1; 162 | constraints.weightx = 0.33; 163 | inputRow.add(selectButton, constraints); 164 | 165 | formPanel.add(inputRow); 166 | return entry; 167 | } 168 | 169 | /** 170 | * Builds the main component panel which wraps formPanel and includes a JTextArea where user can specify additional args, the Run button, and displays warnings for when user interacts with unsupported Find/Avoid feature. 171 | */ 172 | private void buildMainPanel() { 173 | mainPanel = new JPanel(new BorderLayout()); 174 | mainPanel.setMinimumSize(new Dimension(900, 800)); 175 | 176 | mainPanel.add(formPanel, BorderLayout.CENTER); 177 | 178 | JPanel bottomPanel = new JPanel(new BorderLayout()); 179 | 180 | JPanel moreArgsPanel = new JPanel(new BorderLayout()); 181 | 182 | moreArgsPanel.add(new JLabel("Extra Manticore Arguments:"), BorderLayout.NORTH); 183 | 184 | JTextArea moreArgs = new JTextArea(); 185 | moreArgs.setLineWrap(true); 186 | moreArgs.setWrapStyleWord(true); 187 | moreArgsPanel.add(moreArgs, BorderLayout.CENTER); 188 | 189 | setupHookList = new MUIHookListComponent(); 190 | setupHookList.setSize(new Dimension(900, 100)); 191 | setupHookList.setMaximumSize(new Dimension(900, 300)); 192 | moreArgsPanel.add(setupHookList, BorderLayout.SOUTH); 193 | 194 | bottomPanel.add(moreArgsPanel, BorderLayout.CENTER); 195 | 196 | JButton runBtn = new JButton("Run"); 197 | runBtn.addActionListener( 198 | new ActionListener() { 199 | 200 | @Override 201 | public void actionPerformed(ActionEvent e) { 202 | 203 | NativeArguments mcoreArgs = NativeArguments.newBuilder() 204 | .setProgramPath(programPath) 205 | .addAllBinaryArgs(tokenizeArrayInput(formOptions.get("argv").getText())) 206 | .addAllEnvp(tokenizeArrayInput(formOptions.get("env").getText())) 207 | .addAllSymbolicFiles( 208 | tokenizeArrayInput(formOptions.get("file").getText())) 209 | .setStdinSize(formOptions.get("native.stdin_size").getText()) 210 | .setConcreteStart(formOptions.get("data").getText()) 211 | .setAdditionalMcoreArgs(moreArgs.getText()) 212 | .addAllHooks(setupHookList.getAllMUIHooks()) 213 | .build(); 214 | 215 | ManticoreRunner runner = new ManticoreRunner(); 216 | runner.startManticore(mcoreArgs); 217 | 218 | MUIPlugin.log.setVisible(true); 219 | MUIPlugin.stateList.setVisible(true); 220 | MUIPlugin.log.addLogTab(runner); 221 | 222 | setupHookList.clearHooks(); 223 | } 224 | }); 225 | bottomPanel.add(runBtn, BorderLayout.SOUTH); 226 | 227 | mainPanel.add(bottomPanel, BorderLayout.SOUTH); 228 | } 229 | 230 | /** 231 | * Tokenizes a String containing multiple arguments formatted shell-style, cognizant of spaces which are escaped or within quotes. 232 | * @param string Shell-style space-separated arguments for Manticore arguments with array input type. 233 | * @return String iterable suitable to be passed to a ProcessBuilder. 234 | */ 235 | private List tokenizeArrayInput(String string) { 236 | final List WORD_DELIMITERS = Arrays.asList(' ', '\t'); 237 | final List QUOTE_CHARACTERS = Arrays.asList('"', '\''); 238 | final char ESCAPE_CHARACTER = '\\'; 239 | 240 | StringBuilder wordBuilder = new StringBuilder(); 241 | List words = new ArrayList<>(); 242 | char quote = 0; 243 | 244 | for (int i = 0; i < string.length(); i++) { 245 | char c = string.charAt(i); 246 | 247 | if (c == ESCAPE_CHARACTER && i + 1 < string.length()) { 248 | wordBuilder.append(string.charAt(++i)); 249 | } 250 | else if (WORD_DELIMITERS.contains(c) && quote == 0) { 251 | words.add(wordBuilder.toString()); 252 | wordBuilder.setLength(0); 253 | } 254 | else if (quote == 0 && QUOTE_CHARACTERS.contains(c)) { 255 | quote = c; 256 | } 257 | else if (quote == c) { 258 | quote = 0; 259 | } 260 | else { 261 | wordBuilder.append(c); 262 | } 263 | } 264 | 265 | if (wordBuilder.length() > 0) { 266 | words.add(wordBuilder.toString()); 267 | } 268 | 269 | return words; 270 | } 271 | 272 | /** 273 | * Called once the binary being analyzed in Ghidra has been activated. 274 | * @param p the binary being analyzed in Ghidra 275 | * @see MUIPlugin#programActivated(Program) 276 | */ 277 | public void setProgram(Program p) { 278 | program = p; 279 | programPath = program.getExecutablePath(); 280 | } 281 | 282 | @Override 283 | public JComponent getComponent() { 284 | return mainPanel; 285 | } 286 | 287 | } 288 | -------------------------------------------------------------------------------- /MUI/src/main/java/mui/MUIStateListProvider.java: -------------------------------------------------------------------------------- 1 | package mui; 2 | 3 | import java.awt.BorderLayout; 4 | import java.util.HashSet; 5 | 6 | import javax.swing.JComponent; 7 | import javax.swing.JPanel; 8 | import javax.swing.JScrollPane; 9 | import javax.swing.JTree; 10 | import javax.swing.event.TreeExpansionEvent; 11 | import javax.swing.event.TreeExpansionListener; 12 | import javax.swing.tree.DefaultMutableTreeNode; 13 | import javax.swing.tree.DefaultTreeModel; 14 | import javax.swing.tree.TreePath; 15 | 16 | import docking.WindowPosition; 17 | import ghidra.framework.plugintool.ComponentProviderAdapter; 18 | import ghidra.framework.plugintool.PluginTool; 19 | import muicore.MUICore.MUIState; 20 | 21 | /** 22 | * Provides the "MUI State List" component used to display the State List of the Manticore instance whose MUI Log tab is currently focused. 23 | */ 24 | public class MUIStateListProvider extends ComponentProviderAdapter { 25 | 26 | private JPanel mainPanel; 27 | private static JTree stateListTree; 28 | private static DefaultTreeModel treeModel; 29 | private static JScrollPane stateListView; 30 | 31 | private static DefaultMutableTreeNode rootNode; 32 | private static DefaultMutableTreeNode activeNode; 33 | private static DefaultMutableTreeNode waitingNode; 34 | private static DefaultMutableTreeNode forkedNode; 35 | private static DefaultMutableTreeNode completeNode; 36 | private static DefaultMutableTreeNode erroredNode; 37 | 38 | public static ManticoreRunner runnerDisplayed; 39 | 40 | private static int maxStateId; 41 | private static HashSet numsSent; 42 | 43 | public MUIStateListProvider(PluginTool tool, String name) { 44 | super(tool, name, name); 45 | buildStateListView(); 46 | buildMainPanel(); 47 | setTitle("MUI State List"); 48 | setDefaultWindowPosition(WindowPosition.RIGHT); 49 | setVisible(false); 50 | } 51 | 52 | /** 53 | * Builds main component panel which contains the JTree display. 54 | */ 55 | private void buildMainPanel() { 56 | mainPanel = new JPanel(new BorderLayout()); 57 | mainPanel.add(stateListView, BorderLayout.CENTER); 58 | } 59 | 60 | /** 61 | * Builds scrollable tree display with states categorized into the 5 statuses. 62 | */ 63 | private void buildStateListView() { 64 | rootNode = 65 | new DefaultMutableTreeNode("States"); 66 | 67 | activeNode = new DefaultMutableTreeNode("Active"); 68 | waitingNode = new DefaultMutableTreeNode("Waiting"); 69 | forkedNode = new DefaultMutableTreeNode("Forked"); 70 | completeNode = new DefaultMutableTreeNode("Complete"); 71 | erroredNode = new DefaultMutableTreeNode("Errored"); 72 | 73 | rootNode.add(activeNode); 74 | rootNode.add(waitingNode); 75 | rootNode.add(forkedNode); 76 | rootNode.add(completeNode); 77 | rootNode.add(erroredNode); 78 | 79 | treeModel = new DefaultTreeModel(rootNode); 80 | 81 | stateListTree = new JTree(treeModel); 82 | stateListView = new JScrollPane(stateListTree); 83 | 84 | stateListTree.addTreeExpansionListener(new TreeExpansionListener() { 85 | 86 | @Override 87 | public void treeCollapsed(TreeExpansionEvent e) { 88 | runnerDisplayed.stateListExpandedPaths.remove(e.getPath()); 89 | } 90 | 91 | @Override 92 | public void treeExpanded(TreeExpansionEvent e) { 93 | runnerDisplayed.stateListExpandedPaths.add(e.getPath()); 94 | } 95 | 96 | }); 97 | } 98 | 99 | /** 100 | * Gracefully updates the Manticore runner whose State List is shown. 101 | * @param runner The new Manticore runner whose State List should be shown. 102 | */ 103 | public static void changeRunner(ManticoreRunner runner) { 104 | clearStateTree(); 105 | runnerDisplayed = runner; 106 | updateShownStates(runnerDisplayed); 107 | } 108 | 109 | /** 110 | * Helper method to clear all states from the state tree in preparation for a new update. 111 | */ 112 | private static void clearStateTree() { 113 | activeNode.removeAllChildren(); 114 | waitingNode.removeAllChildren(); 115 | forkedNode.removeAllChildren(); 116 | completeNode.removeAllChildren(); 117 | erroredNode.removeAllChildren(); 118 | } 119 | 120 | /** 121 | * * @param stateListModel Updated State List model. 122 | */ 123 | /** 124 | * Updates the State List UI by getting the given Runner's state lists. 125 | * @param runner ManticoreRunner whose states to display 126 | */ 127 | public static void updateShownStates(ManticoreRunner runner) { 128 | 129 | maxStateId = 0; 130 | numsSent = new HashSet(); 131 | numsSent.clear(); 132 | 133 | clearStateTree(); 134 | 135 | runner.getActiveStates().forEach((state) -> activeNode.add(stateToNode(state))); 136 | runner.getWaitingStates().forEach((state) -> waitingNode.add(stateToNode(state))); 137 | runner.getForkedStates().forEach((state) -> forkedNode.add(stateToNode(state))); 138 | runner.getErroredStates().forEach((state) -> erroredNode.add(stateToNode(state))); 139 | runner.getCompleteStates().forEach((state) -> completeNode.add(stateToNode(state))); 140 | 141 | int activeCount = activeNode.getChildCount(); 142 | int waitingCount = waitingNode.getChildCount(); 143 | int forkedCount = forkedNode.getChildCount(); 144 | int erroredCount = erroredNode.getChildCount(); 145 | int completeCount = completeNode.getChildCount(); 146 | 147 | activeNode.setUserObject(String.format("Active (%d)", activeCount)); 148 | waitingNode.setUserObject(String.format("Waiting (%d)", waitingCount)); 149 | forkedNode.setUserObject(String.format("Forked (%d)", forkedCount)); 150 | erroredNode.setUserObject(String.format("Errored (%d)", erroredCount)); 151 | completeNode.setUserObject(String.format("Complete (%d)", completeCount)); 152 | 153 | rootNode.setUserObject(String.format("States (%d)", 154 | activeCount + waitingCount + forkedCount + erroredCount + completeCount)); 155 | 156 | treeModel.reload(); 157 | 158 | for (TreePath path : runner.stateListExpandedPaths) { 159 | stateListTree.expandPath(path); 160 | } 161 | } 162 | 163 | /** 164 | * @param st MUIState 165 | * @return Node that can be added to another parent node for the State List UI. 166 | */ 167 | private static DefaultMutableTreeNode stateToNode(MUIState st) { 168 | maxStateId = Math.max(maxStateId, st.getStateId()); 169 | numsSent.add(st.getStateId()); 170 | return new DefaultMutableTreeNode(String.format("State %d", st.getStateId())); 171 | } 172 | 173 | @Override 174 | public JComponent getComponent() { 175 | return mainPanel; 176 | } 177 | 178 | } 179 | -------------------------------------------------------------------------------- /MUI/src/main/java/mui/ManticoreRunner.java: -------------------------------------------------------------------------------- 1 | package mui; 2 | 3 | import muicore.MUICore.NativeArguments; 4 | import muicore.MUICore.MUILogMessage; 5 | import muicore.MUICore.MUIMessageList; 6 | import muicore.MUICore.MUIState; 7 | import muicore.MUICore.MUIStateList; 8 | import muicore.MUICore.ManticoreInstance; 9 | import muicore.MUICore.ManticoreRunningStatus; 10 | import muicore.MUICore.TerminateResponse; 11 | import io.grpc.stub.StreamObserver; 12 | 13 | import java.util.ArrayList; 14 | import java.util.HashSet; 15 | import java.util.List; 16 | 17 | import javax.swing.JButton; 18 | import javax.swing.JTextArea; 19 | import javax.swing.JToggleButton; 20 | import javax.swing.tree.TreePath; 21 | 22 | import ghidra.util.Msg; 23 | 24 | /** 25 | * The class representing each instance of Manticore. Used to interact with MUI-Core server and correspondingly update UI elements. 26 | */ 27 | public class ManticoreRunner { 28 | 29 | private ManticoreInstance manticoreInstance; 30 | 31 | private boolean hasStarted; 32 | private boolean isRunning; 33 | private boolean wasTerminated; 34 | 35 | private JTextArea logText; 36 | private JButton stopBtn; 37 | private JToggleButton scrollLockBtn; 38 | 39 | private List activeStates; 40 | private List waitingStates; 41 | private List forkedStates; 42 | 43 | private List erroredStates; 44 | private List completeStates; 45 | 46 | public HashSet stateListExpandedPaths; 47 | 48 | public ManticoreRunner() { 49 | hasStarted = false; 50 | isRunning = false; 51 | wasTerminated = false; 52 | 53 | logText = new JTextArea(); 54 | stopBtn = new JButton(); 55 | scrollLockBtn = new JToggleButton(); 56 | 57 | activeStates = new ArrayList(); 58 | waitingStates = new ArrayList(); 59 | forkedStates = new ArrayList(); 60 | erroredStates = new ArrayList(); 61 | completeStates = new ArrayList(); 62 | 63 | stateListExpandedPaths = new HashSet(); 64 | } 65 | 66 | /** 67 | * Starts Manticore with given arguments. 68 | * @param nativeArgs MUICore.NativeArguments object 69 | */ 70 | public void startManticore(NativeArguments nativeArgs) { 71 | 72 | StreamObserver startObserver = new StreamObserver() { 73 | 74 | @Override 75 | public void onCompleted() { 76 | } 77 | 78 | @Override 79 | public void onError(Throwable arg0) { 80 | logText.append(arg0.getMessage() + System.lineSeparator()); 81 | } 82 | 83 | @Override 84 | public void onNext(ManticoreInstance mcore) { 85 | setManticoreInstance(mcore); 86 | setHasStarted(true); 87 | setIsRunning(true); 88 | } 89 | 90 | }; 91 | 92 | MUIPlugin.asyncMUICoreStub.startNative(nativeArgs, startObserver); 93 | } 94 | 95 | public void setManticoreInstance(ManticoreInstance m) { 96 | manticoreInstance = m; 97 | } 98 | 99 | public void setIsRunning(boolean b) { 100 | isRunning = b; 101 | } 102 | 103 | public boolean getHasStarted() { 104 | return hasStarted; 105 | } 106 | 107 | public void setHasStarted(boolean b) { 108 | hasStarted = b; 109 | } 110 | 111 | /** 112 | * Terminates Manticore, but ManticoreRunner instance stays intact and can continue to display its Logs and State List. 113 | */ 114 | public void terminateManticore() { 115 | 116 | StreamObserver terminateObserver = 117 | new StreamObserver() { 118 | 119 | public boolean errored = false; 120 | 121 | @Override 122 | public void onCompleted() { 123 | if (!errored) { 124 | setIsRunning(false); 125 | setWasTerminated(true); 126 | } 127 | } 128 | 129 | @Override 130 | public void onError(Throwable arg0) { 131 | logText.append(arg0.getMessage() + System.lineSeparator()); 132 | errored = true; 133 | } 134 | 135 | @Override 136 | public void onNext(TerminateResponse resp) { 137 | } 138 | 139 | }; 140 | MUIPlugin.asyncMUICoreStub.terminate(manticoreInstance, terminateObserver); 141 | } 142 | 143 | public boolean getWasTerminated() { 144 | return wasTerminated; 145 | } 146 | 147 | public void setWasTerminated(boolean b) { 148 | wasTerminated = b; 149 | } 150 | 151 | /** 152 | * Fetches Message Logs. 153 | * TODO: Message logs displayed should be updated to mimic MUI-Binja. 154 | * TODO: Message logs gRPC service should be re-written to be client-side streaming, not unary 155 | */ 156 | public void fetchMessageLogs() { 157 | 158 | StreamObserver messageListObserver = 159 | new StreamObserver() { 160 | 161 | @Override 162 | public void onCompleted() { 163 | } 164 | 165 | @Override 166 | public void onError(Throwable arg0) { 167 | logText.append(arg0.getMessage() + System.lineSeparator()); 168 | } 169 | 170 | @Override 171 | public void onNext(MUIMessageList messageList) { 172 | for (MUILogMessage msg : messageList.getMessagesList()) { 173 | logText.append(msg.getContent() + System.lineSeparator()); 174 | } 175 | if (!scrollLockBtn.isSelected()) { 176 | logText.setCaretPosition(logText.getText().length()); 177 | } 178 | } 179 | }; 180 | 181 | MUIPlugin.asyncMUICoreStub.getMessageList(manticoreInstance, messageListObserver); 182 | } 183 | 184 | public JTextArea getLogText() { 185 | return logText; 186 | } 187 | 188 | public JButton getStopBtn() { 189 | return stopBtn; 190 | } 191 | 192 | public void setLogUIElems(MUILogContentComponent content) { 193 | logText = content.logArea; 194 | stopBtn = content.stopButton; 195 | scrollLockBtn = content.scrollLockButton; 196 | } 197 | 198 | /** 199 | * Fetches State List. 200 | * TODO: State List gRPC service should be re-written to be client-side streaming, not unary 201 | */ 202 | public void fetchStateList() { 203 | 204 | StreamObserver stateListObserver = new StreamObserver() { 205 | 206 | @Override 207 | public void onCompleted() { 208 | } 209 | 210 | @Override 211 | public void onError(Throwable arg0) { 212 | logText.append(arg0.getMessage() + System.lineSeparator()); 213 | } 214 | 215 | @Override 216 | public void onNext(MUIStateList muiStateList) { 217 | activeStates = muiStateList.getActiveStatesList(); 218 | waitingStates = muiStateList.getWaitingStatesList(); 219 | forkedStates = muiStateList.getForkedStatesList(); 220 | erroredStates = muiStateList.getErroredStatesList(); 221 | completeStates = muiStateList.getCompleteStatesList(); 222 | 223 | if (MUIPlugin.stateList.runnerDisplayed == ManticoreRunner.this) { // tab could've changed in between fetch and onNext 224 | MUIPlugin.stateList.updateShownStates(ManticoreRunner.this); 225 | } 226 | 227 | } 228 | 229 | }; 230 | 231 | MUIPlugin.asyncMUICoreStub.getStateList(manticoreInstance, stateListObserver); 232 | } 233 | 234 | public List getActiveStates() { 235 | return activeStates; 236 | } 237 | 238 | public List getWaitingStates() { 239 | return waitingStates; 240 | } 241 | 242 | public List getForkedStates() { 243 | return forkedStates; 244 | } 245 | 246 | public List getErroredStates() { 247 | return erroredStates; 248 | } 249 | 250 | public List getCompleteStates() { 251 | return completeStates; 252 | } 253 | 254 | /** 255 | * Fetches current status of Manticore execution. 256 | */ 257 | public void fetchIsRunning() { 258 | 259 | StreamObserver runningObserver = 260 | new StreamObserver() { 261 | 262 | @Override 263 | public void onCompleted() { 264 | } 265 | 266 | @Override 267 | public void onError(Throwable arg0) { 268 | logText.append(arg0.getMessage() + System.lineSeparator()); 269 | } 270 | 271 | @Override 272 | public void onNext(ManticoreRunningStatus status) { 273 | isRunning = status.getIsRunning(); 274 | } 275 | 276 | }; 277 | MUIPlugin.asyncMUICoreStub.checkManticoreRunning(manticoreInstance, runningObserver); 278 | } 279 | 280 | public boolean getIsRunning() { 281 | return isRunning; 282 | } 283 | 284 | } 285 | -------------------------------------------------------------------------------- /MUI/src/main/proto/MUICore.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | package muicore; 4 | 5 | service ManticoreUI { 6 | rpc StartNative(NativeArguments) returns (ManticoreInstance) {} 7 | rpc StartEVM(EVMArguments) returns (ManticoreInstance) {} 8 | rpc Terminate(ManticoreInstance) returns (TerminateResponse) {} 9 | rpc GetStateList(ManticoreInstance) returns (MUIStateList) {} 10 | rpc GetMessageList(ManticoreInstance) returns (MUIMessageList) {} 11 | rpc CheckManticoreRunning(ManticoreInstance) returns (ManticoreRunningStatus) {} 12 | rpc StopServer(StopServerRequest) returns (StopServerResponse) {} 13 | } 14 | 15 | // LogMessage and StateList message types have "MUI" in their names to distinguish them from those in mserialize 16 | 17 | message MUILogMessage{ 18 | string content = 1; 19 | } 20 | 21 | message MUIMessageList{ 22 | repeated MUILogMessage messages = 2; 23 | } 24 | 25 | message MUIState { 26 | int32 state_id = 3; 27 | uint64 pc = 10; 28 | int32 parent_id = 28; 29 | repeated int32 children_ids = 29; 30 | } 31 | 32 | message MUIStateList{ 33 | 34 | // state categories in MUI - based on manticore enums StateStatus and StateList 35 | repeated MUIState active_states = 4; 36 | repeated MUIState waiting_states = 5; 37 | repeated MUIState forked_states = 6; 38 | repeated MUIState errored_states = 7; 39 | repeated MUIState complete_states = 8; 40 | } 41 | 42 | message ManticoreInstance { 43 | string uuid = 9; 44 | } 45 | 46 | message TerminateResponse {} 47 | 48 | message Hook { 49 | 50 | enum HookType { 51 | FIND = 0; 52 | AVOID = 1; 53 | CUSTOM = 2; 54 | GLOBAL = 3; 55 | } 56 | 57 | uint64 address = 26; 58 | HookType type = 27; 59 | string func_text = 31; 60 | } 61 | 62 | message NativeArguments { 63 | string program_path = 11; 64 | repeated string binary_args = 16; 65 | repeated string envp = 17; 66 | repeated string symbolic_files = 18; 67 | string concrete_start = 19; 68 | string stdin_size = 20; 69 | string additional_mcore_args = 21; 70 | repeated Hook hooks = 30; 71 | } 72 | 73 | message EVMArguments { 74 | string contract_path = 12; 75 | string contract_name = 13; 76 | string solc_bin = 14; 77 | string tx_limit = 22; 78 | string tx_account = 23; 79 | repeated string detectors_to_exclude = 24; 80 | string additional_flags = 25; 81 | } 82 | 83 | message ManticoreRunningStatus { 84 | bool is_running = 15; 85 | } 86 | 87 | message StopServerRequest {} 88 | 89 | message StopServerResponse {} -------------------------------------------------------------------------------- /MUICore/README.md: -------------------------------------------------------------------------------- 1 | # MUI-Core 2 | MUI-Core is a gRPC service that acts as a wrapper around [Manticore](https://github.com/trailofbits/manticore), which will serve as the future of the [Manticore User Interface (MUI)](https://github.com/trailofbits/ManticoreUI) project. MUI-Core is designed to allow developers to create Manticore UI Plugins for different disassemblers easily, and to allow for Manticore itself to be done in the cloud/remotely. 3 | 4 | # Usage 5 | The MUI-Core server can be run via `python3 mui_server.py`. For most applications of MUI, in which a Python interpreter available or the required dependencies are not available, users can run the stand-alone `muicore_server` binary. 6 | 7 | Currently, MUI-Core communicates only on `localhost:50010`. 8 | 9 | Your MUI Plugin will require the relevant gRPC client code. You can find out how to generate gRPC client code in your desired language from [the gRPC website](https://grpc.io/docs/languages/). 10 | 11 | You may refer to the [Protobuf Specification](MUICore.proto) for information about the RPC services provided and the message types. 12 | -------------------------------------------------------------------------------- /MUICore/justfile: -------------------------------------------------------------------------------- 1 | format: 2 | black . 3 | isort . 4 | 5 | lint: 6 | black --check . 7 | isort --check . 8 | mypy 9 | 10 | generate: 11 | python3 setup.py generate 12 | 13 | build: generate 14 | mkdir -p dist 15 | shiv -c muicore -o dist/muicore_server --reproducible --no-modify --python '/usr/bin/env python3.9' --compressed . 16 | wget https://github.com/ethereum/solc-bin/raw/gh-pages/linux-amd64/solc-linux-amd64-v0.8.9%2Bcommit.e5eed63a -O dist/solc 17 | chmod 775 dist/solc 18 | 19 | install: build 20 | cp dist/* ../MUI/os/linux_x86_64/ 21 | 22 | test: 23 | python3 -m unittest tests/test_*.py 24 | 25 | -------------------------------------------------------------------------------- /MUICore/muicore/MUICore.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | package muicore; 4 | 5 | service ManticoreUI { 6 | rpc StartNative(NativeArguments) returns (ManticoreInstance) {} 7 | rpc StartEVM(EVMArguments) returns (ManticoreInstance) {} 8 | rpc Terminate(ManticoreInstance) returns (TerminateResponse) {} 9 | rpc GetStateList(ManticoreInstance) returns (MUIStateList) {} 10 | rpc GetMessageList(ManticoreInstance) returns (MUIMessageList) {} 11 | rpc CheckManticoreRunning(ManticoreInstance) returns (ManticoreRunningStatus) {} 12 | rpc StopServer(StopServerRequest) returns (StopServerResponse) {} 13 | } 14 | 15 | // LogMessage and StateList message types have "MUI" in their names to distinguish them from those in mserialize 16 | 17 | message MUILogMessage{ 18 | string content = 1; 19 | } 20 | 21 | message MUIMessageList{ 22 | repeated MUILogMessage messages = 2; 23 | } 24 | 25 | message MUIState { 26 | int32 state_id = 3; 27 | uint64 pc = 10; 28 | int32 parent_id = 28; 29 | repeated int32 children_ids = 29; 30 | } 31 | 32 | message MUIStateList{ 33 | 34 | // state categories in MUI - based on manticore enums StateStatus and StateList 35 | repeated MUIState active_states = 4; 36 | repeated MUIState waiting_states = 5; 37 | repeated MUIState forked_states = 6; 38 | repeated MUIState errored_states = 7; 39 | repeated MUIState complete_states = 8; 40 | } 41 | 42 | message ManticoreInstance { 43 | string uuid = 9; 44 | } 45 | 46 | message TerminateResponse {} 47 | 48 | message Hook { 49 | 50 | enum HookType { 51 | FIND = 0; 52 | AVOID = 1; 53 | CUSTOM = 2; 54 | GLOBAL = 3; 55 | } 56 | 57 | uint64 address = 26; 58 | HookType type = 27; 59 | string func_text = 31; 60 | } 61 | 62 | message NativeArguments { 63 | string program_path = 11; 64 | repeated string binary_args = 16; 65 | repeated string envp = 17; 66 | repeated string symbolic_files = 18; 67 | string concrete_start = 19; 68 | string stdin_size = 20; 69 | string additional_mcore_args = 21; 70 | repeated Hook hooks = 30; 71 | } 72 | 73 | message EVMArguments { 74 | string contract_path = 12; 75 | string contract_name = 13; 76 | string solc_bin = 14; 77 | string tx_limit = 22; 78 | string tx_account = 23; 79 | repeated string detectors_to_exclude = 24; 80 | string additional_flags = 25; 81 | } 82 | 83 | message ManticoreRunningStatus { 84 | bool is_running = 15; 85 | } 86 | 87 | message StopServerRequest {} 88 | 89 | message StopServerResponse {} -------------------------------------------------------------------------------- /MUICore/muicore/MUICore_pb2.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by the protocol buffer compiler. DO NOT EDIT! 3 | # source: muicore/MUICore.proto 4 | """Generated protocol buffer code.""" 5 | from google.protobuf import descriptor as _descriptor 6 | from google.protobuf import descriptor_pool as _descriptor_pool 7 | from google.protobuf import message as _message 8 | from google.protobuf import reflection as _reflection 9 | from google.protobuf import symbol_database as _symbol_database 10 | # @@protoc_insertion_point(imports) 11 | 12 | _sym_db = _symbol_database.Default() 13 | 14 | 15 | 16 | 17 | DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x15muicore/MUICore.proto\x12\x07muicore\" \n\rMUILogMessage\x12\x0f\n\x07\x63ontent\x18\x01 \x01(\t\":\n\x0eMUIMessageList\x12(\n\x08messages\x18\x02 \x03(\x0b\x32\x16.muicore.MUILogMessage\"Q\n\x08MUIState\x12\x10\n\x08state_id\x18\x03 \x01(\x05\x12\n\n\x02pc\x18\n \x01(\x04\x12\x11\n\tparent_id\x18\x1c \x01(\x05\x12\x14\n\x0c\x63hildren_ids\x18\x1d \x03(\x05\"\xe4\x01\n\x0cMUIStateList\x12(\n\ractive_states\x18\x04 \x03(\x0b\x32\x11.muicore.MUIState\x12)\n\x0ewaiting_states\x18\x05 \x03(\x0b\x32\x11.muicore.MUIState\x12(\n\rforked_states\x18\x06 \x03(\x0b\x32\x11.muicore.MUIState\x12)\n\x0e\x65rrored_states\x18\x07 \x03(\x0b\x32\x11.muicore.MUIState\x12*\n\x0f\x63omplete_states\x18\x08 \x03(\x0b\x32\x11.muicore.MUIState\"!\n\x11ManticoreInstance\x12\x0c\n\x04uuid\x18\t \x01(\t\"\x13\n\x11TerminateResponse\"\x89\x01\n\x04Hook\x12\x0f\n\x07\x61\x64\x64ress\x18\x1a \x01(\x04\x12$\n\x04type\x18\x1b \x01(\x0e\x32\x16.muicore.Hook.HookType\x12\x11\n\tfunc_text\x18\x1f \x01(\t\"7\n\x08HookType\x12\x08\n\x04\x46IND\x10\x00\x12\t\n\x05\x41VOID\x10\x01\x12\n\n\x06\x43USTOM\x10\x02\x12\n\n\x06GLOBAL\x10\x03\"\xcb\x01\n\x0fNativeArguments\x12\x14\n\x0cprogram_path\x18\x0b \x01(\t\x12\x13\n\x0b\x62inary_args\x18\x10 \x03(\t\x12\x0c\n\x04\x65nvp\x18\x11 \x03(\t\x12\x16\n\x0esymbolic_files\x18\x12 \x03(\t\x12\x16\n\x0e\x63oncrete_start\x18\x13 \x01(\t\x12\x12\n\nstdin_size\x18\x14 \x01(\t\x12\x1d\n\x15\x61\x64\x64itional_mcore_args\x18\x15 \x01(\t\x12\x1c\n\x05hooks\x18\x1e \x03(\x0b\x32\r.muicore.Hook\"\xac\x01\n\x0c\x45VMArguments\x12\x15\n\rcontract_path\x18\x0c \x01(\t\x12\x15\n\rcontract_name\x18\r \x01(\t\x12\x10\n\x08solc_bin\x18\x0e \x01(\t\x12\x10\n\x08tx_limit\x18\x16 \x01(\t\x12\x12\n\ntx_account\x18\x17 \x01(\t\x12\x1c\n\x14\x64\x65tectors_to_exclude\x18\x18 \x03(\t\x12\x18\n\x10\x61\x64\x64itional_flags\x18\x19 \x01(\t\",\n\x16ManticoreRunningStatus\x12\x12\n\nis_running\x18\x0f \x01(\x08\"\x13\n\x11StopServerRequest\"\x14\n\x12StopServerResponse2\x8b\x04\n\x0bManticoreUI\x12\x45\n\x0bStartNative\x12\x18.muicore.NativeArguments\x1a\x1a.muicore.ManticoreInstance\"\x00\x12?\n\x08StartEVM\x12\x15.muicore.EVMArguments\x1a\x1a.muicore.ManticoreInstance\"\x00\x12\x45\n\tTerminate\x12\x1a.muicore.ManticoreInstance\x1a\x1a.muicore.TerminateResponse\"\x00\x12\x43\n\x0cGetStateList\x12\x1a.muicore.ManticoreInstance\x1a\x15.muicore.MUIStateList\"\x00\x12G\n\x0eGetMessageList\x12\x1a.muicore.ManticoreInstance\x1a\x17.muicore.MUIMessageList\"\x00\x12V\n\x15\x43heckManticoreRunning\x12\x1a.muicore.ManticoreInstance\x1a\x1f.muicore.ManticoreRunningStatus\"\x00\x12G\n\nStopServer\x12\x1a.muicore.StopServerRequest\x1a\x1b.muicore.StopServerResponse\"\x00\x62\x06proto3') 18 | 19 | 20 | 21 | _MUILOGMESSAGE = DESCRIPTOR.message_types_by_name['MUILogMessage'] 22 | _MUIMESSAGELIST = DESCRIPTOR.message_types_by_name['MUIMessageList'] 23 | _MUISTATE = DESCRIPTOR.message_types_by_name['MUIState'] 24 | _MUISTATELIST = DESCRIPTOR.message_types_by_name['MUIStateList'] 25 | _MANTICOREINSTANCE = DESCRIPTOR.message_types_by_name['ManticoreInstance'] 26 | _TERMINATERESPONSE = DESCRIPTOR.message_types_by_name['TerminateResponse'] 27 | _HOOK = DESCRIPTOR.message_types_by_name['Hook'] 28 | _NATIVEARGUMENTS = DESCRIPTOR.message_types_by_name['NativeArguments'] 29 | _EVMARGUMENTS = DESCRIPTOR.message_types_by_name['EVMArguments'] 30 | _MANTICORERUNNINGSTATUS = DESCRIPTOR.message_types_by_name['ManticoreRunningStatus'] 31 | _STOPSERVERREQUEST = DESCRIPTOR.message_types_by_name['StopServerRequest'] 32 | _STOPSERVERRESPONSE = DESCRIPTOR.message_types_by_name['StopServerResponse'] 33 | _HOOK_HOOKTYPE = _HOOK.enum_types_by_name['HookType'] 34 | MUILogMessage = _reflection.GeneratedProtocolMessageType('MUILogMessage', (_message.Message,), { 35 | 'DESCRIPTOR' : _MUILOGMESSAGE, 36 | '__module__' : 'muicore.MUICore_pb2' 37 | # @@protoc_insertion_point(class_scope:muicore.MUILogMessage) 38 | }) 39 | _sym_db.RegisterMessage(MUILogMessage) 40 | 41 | MUIMessageList = _reflection.GeneratedProtocolMessageType('MUIMessageList', (_message.Message,), { 42 | 'DESCRIPTOR' : _MUIMESSAGELIST, 43 | '__module__' : 'muicore.MUICore_pb2' 44 | # @@protoc_insertion_point(class_scope:muicore.MUIMessageList) 45 | }) 46 | _sym_db.RegisterMessage(MUIMessageList) 47 | 48 | MUIState = _reflection.GeneratedProtocolMessageType('MUIState', (_message.Message,), { 49 | 'DESCRIPTOR' : _MUISTATE, 50 | '__module__' : 'muicore.MUICore_pb2' 51 | # @@protoc_insertion_point(class_scope:muicore.MUIState) 52 | }) 53 | _sym_db.RegisterMessage(MUIState) 54 | 55 | MUIStateList = _reflection.GeneratedProtocolMessageType('MUIStateList', (_message.Message,), { 56 | 'DESCRIPTOR' : _MUISTATELIST, 57 | '__module__' : 'muicore.MUICore_pb2' 58 | # @@protoc_insertion_point(class_scope:muicore.MUIStateList) 59 | }) 60 | _sym_db.RegisterMessage(MUIStateList) 61 | 62 | ManticoreInstance = _reflection.GeneratedProtocolMessageType('ManticoreInstance', (_message.Message,), { 63 | 'DESCRIPTOR' : _MANTICOREINSTANCE, 64 | '__module__' : 'muicore.MUICore_pb2' 65 | # @@protoc_insertion_point(class_scope:muicore.ManticoreInstance) 66 | }) 67 | _sym_db.RegisterMessage(ManticoreInstance) 68 | 69 | TerminateResponse = _reflection.GeneratedProtocolMessageType('TerminateResponse', (_message.Message,), { 70 | 'DESCRIPTOR' : _TERMINATERESPONSE, 71 | '__module__' : 'muicore.MUICore_pb2' 72 | # @@protoc_insertion_point(class_scope:muicore.TerminateResponse) 73 | }) 74 | _sym_db.RegisterMessage(TerminateResponse) 75 | 76 | Hook = _reflection.GeneratedProtocolMessageType('Hook', (_message.Message,), { 77 | 'DESCRIPTOR' : _HOOK, 78 | '__module__' : 'muicore.MUICore_pb2' 79 | # @@protoc_insertion_point(class_scope:muicore.Hook) 80 | }) 81 | _sym_db.RegisterMessage(Hook) 82 | 83 | NativeArguments = _reflection.GeneratedProtocolMessageType('NativeArguments', (_message.Message,), { 84 | 'DESCRIPTOR' : _NATIVEARGUMENTS, 85 | '__module__' : 'muicore.MUICore_pb2' 86 | # @@protoc_insertion_point(class_scope:muicore.NativeArguments) 87 | }) 88 | _sym_db.RegisterMessage(NativeArguments) 89 | 90 | EVMArguments = _reflection.GeneratedProtocolMessageType('EVMArguments', (_message.Message,), { 91 | 'DESCRIPTOR' : _EVMARGUMENTS, 92 | '__module__' : 'muicore.MUICore_pb2' 93 | # @@protoc_insertion_point(class_scope:muicore.EVMArguments) 94 | }) 95 | _sym_db.RegisterMessage(EVMArguments) 96 | 97 | ManticoreRunningStatus = _reflection.GeneratedProtocolMessageType('ManticoreRunningStatus', (_message.Message,), { 98 | 'DESCRIPTOR' : _MANTICORERUNNINGSTATUS, 99 | '__module__' : 'muicore.MUICore_pb2' 100 | # @@protoc_insertion_point(class_scope:muicore.ManticoreRunningStatus) 101 | }) 102 | _sym_db.RegisterMessage(ManticoreRunningStatus) 103 | 104 | StopServerRequest = _reflection.GeneratedProtocolMessageType('StopServerRequest', (_message.Message,), { 105 | 'DESCRIPTOR' : _STOPSERVERREQUEST, 106 | '__module__' : 'muicore.MUICore_pb2' 107 | # @@protoc_insertion_point(class_scope:muicore.StopServerRequest) 108 | }) 109 | _sym_db.RegisterMessage(StopServerRequest) 110 | 111 | StopServerResponse = _reflection.GeneratedProtocolMessageType('StopServerResponse', (_message.Message,), { 112 | 'DESCRIPTOR' : _STOPSERVERRESPONSE, 113 | '__module__' : 'muicore.MUICore_pb2' 114 | # @@protoc_insertion_point(class_scope:muicore.StopServerResponse) 115 | }) 116 | _sym_db.RegisterMessage(StopServerResponse) 117 | 118 | _MANTICOREUI = DESCRIPTOR.services_by_name['ManticoreUI'] 119 | if _descriptor._USE_C_DESCRIPTORS == False: 120 | 121 | DESCRIPTOR._options = None 122 | _MUILOGMESSAGE._serialized_start=34 123 | _MUILOGMESSAGE._serialized_end=66 124 | _MUIMESSAGELIST._serialized_start=68 125 | _MUIMESSAGELIST._serialized_end=126 126 | _MUISTATE._serialized_start=128 127 | _MUISTATE._serialized_end=209 128 | _MUISTATELIST._serialized_start=212 129 | _MUISTATELIST._serialized_end=440 130 | _MANTICOREINSTANCE._serialized_start=442 131 | _MANTICOREINSTANCE._serialized_end=475 132 | _TERMINATERESPONSE._serialized_start=477 133 | _TERMINATERESPONSE._serialized_end=496 134 | _HOOK._serialized_start=499 135 | _HOOK._serialized_end=636 136 | _HOOK_HOOKTYPE._serialized_start=581 137 | _HOOK_HOOKTYPE._serialized_end=636 138 | _NATIVEARGUMENTS._serialized_start=639 139 | _NATIVEARGUMENTS._serialized_end=842 140 | _EVMARGUMENTS._serialized_start=845 141 | _EVMARGUMENTS._serialized_end=1017 142 | _MANTICORERUNNINGSTATUS._serialized_start=1019 143 | _MANTICORERUNNINGSTATUS._serialized_end=1063 144 | _STOPSERVERREQUEST._serialized_start=1065 145 | _STOPSERVERREQUEST._serialized_end=1084 146 | _STOPSERVERRESPONSE._serialized_start=1086 147 | _STOPSERVERRESPONSE._serialized_end=1106 148 | _MANTICOREUI._serialized_start=1109 149 | _MANTICOREUI._serialized_end=1632 150 | # @@protoc_insertion_point(module_scope) 151 | -------------------------------------------------------------------------------- /MUICore/muicore/MUICore_pb2.pyi: -------------------------------------------------------------------------------- 1 | """ 2 | @generated by mypy-protobuf. Do not edit manually! 3 | isort:skip_file 4 | """ 5 | import builtins 6 | import google.protobuf.descriptor 7 | import google.protobuf.internal.containers 8 | import google.protobuf.internal.enum_type_wrapper 9 | import google.protobuf.message 10 | import typing 11 | import typing_extensions 12 | 13 | DESCRIPTOR: google.protobuf.descriptor.FileDescriptor 14 | 15 | class MUILogMessage(google.protobuf.message.Message): 16 | """LogMessage and StateList message types have "MUI" in their names to distinguish them from those in mserialize 17 | 18 | """ 19 | DESCRIPTOR: google.protobuf.descriptor.Descriptor 20 | CONTENT_FIELD_NUMBER: builtins.int 21 | content: typing.Text 22 | def __init__(self, 23 | *, 24 | content: typing.Text = ..., 25 | ) -> None: ... 26 | def ClearField(self, field_name: typing_extensions.Literal["content",b"content"]) -> None: ... 27 | global___MUILogMessage = MUILogMessage 28 | 29 | class MUIMessageList(google.protobuf.message.Message): 30 | DESCRIPTOR: google.protobuf.descriptor.Descriptor 31 | MESSAGES_FIELD_NUMBER: builtins.int 32 | @property 33 | def messages(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[global___MUILogMessage]: ... 34 | def __init__(self, 35 | *, 36 | messages: typing.Optional[typing.Iterable[global___MUILogMessage]] = ..., 37 | ) -> None: ... 38 | def ClearField(self, field_name: typing_extensions.Literal["messages",b"messages"]) -> None: ... 39 | global___MUIMessageList = MUIMessageList 40 | 41 | class MUIState(google.protobuf.message.Message): 42 | DESCRIPTOR: google.protobuf.descriptor.Descriptor 43 | STATE_ID_FIELD_NUMBER: builtins.int 44 | PC_FIELD_NUMBER: builtins.int 45 | PARENT_ID_FIELD_NUMBER: builtins.int 46 | CHILDREN_IDS_FIELD_NUMBER: builtins.int 47 | state_id: builtins.int 48 | pc: builtins.int 49 | parent_id: builtins.int 50 | @property 51 | def children_ids(self) -> google.protobuf.internal.containers.RepeatedScalarFieldContainer[builtins.int]: ... 52 | def __init__(self, 53 | *, 54 | state_id: builtins.int = ..., 55 | pc: builtins.int = ..., 56 | parent_id: builtins.int = ..., 57 | children_ids: typing.Optional[typing.Iterable[builtins.int]] = ..., 58 | ) -> None: ... 59 | def ClearField(self, field_name: typing_extensions.Literal["children_ids",b"children_ids","parent_id",b"parent_id","pc",b"pc","state_id",b"state_id"]) -> None: ... 60 | global___MUIState = MUIState 61 | 62 | class MUIStateList(google.protobuf.message.Message): 63 | DESCRIPTOR: google.protobuf.descriptor.Descriptor 64 | ACTIVE_STATES_FIELD_NUMBER: builtins.int 65 | WAITING_STATES_FIELD_NUMBER: builtins.int 66 | FORKED_STATES_FIELD_NUMBER: builtins.int 67 | ERRORED_STATES_FIELD_NUMBER: builtins.int 68 | COMPLETE_STATES_FIELD_NUMBER: builtins.int 69 | @property 70 | def active_states(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[global___MUIState]: 71 | """state categories in MUI - based on manticore enums StateStatus and StateList""" 72 | pass 73 | @property 74 | def waiting_states(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[global___MUIState]: ... 75 | @property 76 | def forked_states(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[global___MUIState]: ... 77 | @property 78 | def errored_states(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[global___MUIState]: ... 79 | @property 80 | def complete_states(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[global___MUIState]: ... 81 | def __init__(self, 82 | *, 83 | active_states: typing.Optional[typing.Iterable[global___MUIState]] = ..., 84 | waiting_states: typing.Optional[typing.Iterable[global___MUIState]] = ..., 85 | forked_states: typing.Optional[typing.Iterable[global___MUIState]] = ..., 86 | errored_states: typing.Optional[typing.Iterable[global___MUIState]] = ..., 87 | complete_states: typing.Optional[typing.Iterable[global___MUIState]] = ..., 88 | ) -> None: ... 89 | def ClearField(self, field_name: typing_extensions.Literal["active_states",b"active_states","complete_states",b"complete_states","errored_states",b"errored_states","forked_states",b"forked_states","waiting_states",b"waiting_states"]) -> None: ... 90 | global___MUIStateList = MUIStateList 91 | 92 | class ManticoreInstance(google.protobuf.message.Message): 93 | DESCRIPTOR: google.protobuf.descriptor.Descriptor 94 | UUID_FIELD_NUMBER: builtins.int 95 | uuid: typing.Text 96 | def __init__(self, 97 | *, 98 | uuid: typing.Text = ..., 99 | ) -> None: ... 100 | def ClearField(self, field_name: typing_extensions.Literal["uuid",b"uuid"]) -> None: ... 101 | global___ManticoreInstance = ManticoreInstance 102 | 103 | class TerminateResponse(google.protobuf.message.Message): 104 | DESCRIPTOR: google.protobuf.descriptor.Descriptor 105 | def __init__(self, 106 | ) -> None: ... 107 | global___TerminateResponse = TerminateResponse 108 | 109 | class Hook(google.protobuf.message.Message): 110 | DESCRIPTOR: google.protobuf.descriptor.Descriptor 111 | class _HookType: 112 | ValueType = typing.NewType('ValueType', builtins.int) 113 | V: typing_extensions.TypeAlias = ValueType 114 | class _HookTypeEnumTypeWrapper(google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[Hook._HookType.ValueType], builtins.type): 115 | DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor 116 | FIND: Hook._HookType.ValueType # 0 117 | AVOID: Hook._HookType.ValueType # 1 118 | CUSTOM: Hook._HookType.ValueType # 2 119 | GLOBAL: Hook._HookType.ValueType # 3 120 | class HookType(_HookType, metaclass=_HookTypeEnumTypeWrapper): 121 | pass 122 | 123 | FIND: Hook.HookType.ValueType # 0 124 | AVOID: Hook.HookType.ValueType # 1 125 | CUSTOM: Hook.HookType.ValueType # 2 126 | GLOBAL: Hook.HookType.ValueType # 3 127 | 128 | ADDRESS_FIELD_NUMBER: builtins.int 129 | TYPE_FIELD_NUMBER: builtins.int 130 | FUNC_TEXT_FIELD_NUMBER: builtins.int 131 | address: builtins.int 132 | type: global___Hook.HookType.ValueType 133 | func_text: typing.Text 134 | def __init__(self, 135 | *, 136 | address: builtins.int = ..., 137 | type: global___Hook.HookType.ValueType = ..., 138 | func_text: typing.Text = ..., 139 | ) -> None: ... 140 | def ClearField(self, field_name: typing_extensions.Literal["address",b"address","func_text",b"func_text","type",b"type"]) -> None: ... 141 | global___Hook = Hook 142 | 143 | class NativeArguments(google.protobuf.message.Message): 144 | DESCRIPTOR: google.protobuf.descriptor.Descriptor 145 | PROGRAM_PATH_FIELD_NUMBER: builtins.int 146 | BINARY_ARGS_FIELD_NUMBER: builtins.int 147 | ENVP_FIELD_NUMBER: builtins.int 148 | SYMBOLIC_FILES_FIELD_NUMBER: builtins.int 149 | CONCRETE_START_FIELD_NUMBER: builtins.int 150 | STDIN_SIZE_FIELD_NUMBER: builtins.int 151 | ADDITIONAL_MCORE_ARGS_FIELD_NUMBER: builtins.int 152 | HOOKS_FIELD_NUMBER: builtins.int 153 | program_path: typing.Text 154 | @property 155 | def binary_args(self) -> google.protobuf.internal.containers.RepeatedScalarFieldContainer[typing.Text]: ... 156 | @property 157 | def envp(self) -> google.protobuf.internal.containers.RepeatedScalarFieldContainer[typing.Text]: ... 158 | @property 159 | def symbolic_files(self) -> google.protobuf.internal.containers.RepeatedScalarFieldContainer[typing.Text]: ... 160 | concrete_start: typing.Text 161 | stdin_size: typing.Text 162 | additional_mcore_args: typing.Text 163 | @property 164 | def hooks(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[global___Hook]: ... 165 | def __init__(self, 166 | *, 167 | program_path: typing.Text = ..., 168 | binary_args: typing.Optional[typing.Iterable[typing.Text]] = ..., 169 | envp: typing.Optional[typing.Iterable[typing.Text]] = ..., 170 | symbolic_files: typing.Optional[typing.Iterable[typing.Text]] = ..., 171 | concrete_start: typing.Text = ..., 172 | stdin_size: typing.Text = ..., 173 | additional_mcore_args: typing.Text = ..., 174 | hooks: typing.Optional[typing.Iterable[global___Hook]] = ..., 175 | ) -> None: ... 176 | def ClearField(self, field_name: typing_extensions.Literal["additional_mcore_args",b"additional_mcore_args","binary_args",b"binary_args","concrete_start",b"concrete_start","envp",b"envp","hooks",b"hooks","program_path",b"program_path","stdin_size",b"stdin_size","symbolic_files",b"symbolic_files"]) -> None: ... 177 | global___NativeArguments = NativeArguments 178 | 179 | class EVMArguments(google.protobuf.message.Message): 180 | DESCRIPTOR: google.protobuf.descriptor.Descriptor 181 | CONTRACT_PATH_FIELD_NUMBER: builtins.int 182 | CONTRACT_NAME_FIELD_NUMBER: builtins.int 183 | SOLC_BIN_FIELD_NUMBER: builtins.int 184 | TX_LIMIT_FIELD_NUMBER: builtins.int 185 | TX_ACCOUNT_FIELD_NUMBER: builtins.int 186 | DETECTORS_TO_EXCLUDE_FIELD_NUMBER: builtins.int 187 | ADDITIONAL_FLAGS_FIELD_NUMBER: builtins.int 188 | contract_path: typing.Text 189 | contract_name: typing.Text 190 | solc_bin: typing.Text 191 | tx_limit: typing.Text 192 | tx_account: typing.Text 193 | @property 194 | def detectors_to_exclude(self) -> google.protobuf.internal.containers.RepeatedScalarFieldContainer[typing.Text]: ... 195 | additional_flags: typing.Text 196 | def __init__(self, 197 | *, 198 | contract_path: typing.Text = ..., 199 | contract_name: typing.Text = ..., 200 | solc_bin: typing.Text = ..., 201 | tx_limit: typing.Text = ..., 202 | tx_account: typing.Text = ..., 203 | detectors_to_exclude: typing.Optional[typing.Iterable[typing.Text]] = ..., 204 | additional_flags: typing.Text = ..., 205 | ) -> None: ... 206 | def ClearField(self, field_name: typing_extensions.Literal["additional_flags",b"additional_flags","contract_name",b"contract_name","contract_path",b"contract_path","detectors_to_exclude",b"detectors_to_exclude","solc_bin",b"solc_bin","tx_account",b"tx_account","tx_limit",b"tx_limit"]) -> None: ... 207 | global___EVMArguments = EVMArguments 208 | 209 | class ManticoreRunningStatus(google.protobuf.message.Message): 210 | DESCRIPTOR: google.protobuf.descriptor.Descriptor 211 | IS_RUNNING_FIELD_NUMBER: builtins.int 212 | is_running: builtins.bool 213 | def __init__(self, 214 | *, 215 | is_running: builtins.bool = ..., 216 | ) -> None: ... 217 | def ClearField(self, field_name: typing_extensions.Literal["is_running",b"is_running"]) -> None: ... 218 | global___ManticoreRunningStatus = ManticoreRunningStatus 219 | 220 | class StopServerRequest(google.protobuf.message.Message): 221 | DESCRIPTOR: google.protobuf.descriptor.Descriptor 222 | def __init__(self, 223 | ) -> None: ... 224 | global___StopServerRequest = StopServerRequest 225 | 226 | class StopServerResponse(google.protobuf.message.Message): 227 | DESCRIPTOR: google.protobuf.descriptor.Descriptor 228 | def __init__(self, 229 | ) -> None: ... 230 | global___StopServerResponse = StopServerResponse 231 | -------------------------------------------------------------------------------- /MUICore/muicore/MUICore_pb2_grpc.py: -------------------------------------------------------------------------------- 1 | # Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT! 2 | """Client and server classes corresponding to protobuf-defined services.""" 3 | import grpc 4 | 5 | from muicore import MUICore_pb2 as muicore_dot_MUICore__pb2 6 | 7 | 8 | class ManticoreUIStub(object): 9 | """Missing associated documentation comment in .proto file.""" 10 | 11 | def __init__(self, channel): 12 | """Constructor. 13 | 14 | Args: 15 | channel: A grpc.Channel. 16 | """ 17 | self.StartNative = channel.unary_unary( 18 | '/muicore.ManticoreUI/StartNative', 19 | request_serializer=muicore_dot_MUICore__pb2.NativeArguments.SerializeToString, 20 | response_deserializer=muicore_dot_MUICore__pb2.ManticoreInstance.FromString, 21 | ) 22 | self.StartEVM = channel.unary_unary( 23 | '/muicore.ManticoreUI/StartEVM', 24 | request_serializer=muicore_dot_MUICore__pb2.EVMArguments.SerializeToString, 25 | response_deserializer=muicore_dot_MUICore__pb2.ManticoreInstance.FromString, 26 | ) 27 | self.Terminate = channel.unary_unary( 28 | '/muicore.ManticoreUI/Terminate', 29 | request_serializer=muicore_dot_MUICore__pb2.ManticoreInstance.SerializeToString, 30 | response_deserializer=muicore_dot_MUICore__pb2.TerminateResponse.FromString, 31 | ) 32 | self.GetStateList = channel.unary_unary( 33 | '/muicore.ManticoreUI/GetStateList', 34 | request_serializer=muicore_dot_MUICore__pb2.ManticoreInstance.SerializeToString, 35 | response_deserializer=muicore_dot_MUICore__pb2.MUIStateList.FromString, 36 | ) 37 | self.GetMessageList = channel.unary_unary( 38 | '/muicore.ManticoreUI/GetMessageList', 39 | request_serializer=muicore_dot_MUICore__pb2.ManticoreInstance.SerializeToString, 40 | response_deserializer=muicore_dot_MUICore__pb2.MUIMessageList.FromString, 41 | ) 42 | self.CheckManticoreRunning = channel.unary_unary( 43 | '/muicore.ManticoreUI/CheckManticoreRunning', 44 | request_serializer=muicore_dot_MUICore__pb2.ManticoreInstance.SerializeToString, 45 | response_deserializer=muicore_dot_MUICore__pb2.ManticoreRunningStatus.FromString, 46 | ) 47 | self.StopServer = channel.unary_unary( 48 | '/muicore.ManticoreUI/StopServer', 49 | request_serializer=muicore_dot_MUICore__pb2.StopServerRequest.SerializeToString, 50 | response_deserializer=muicore_dot_MUICore__pb2.StopServerResponse.FromString, 51 | ) 52 | 53 | 54 | class ManticoreUIServicer(object): 55 | """Missing associated documentation comment in .proto file.""" 56 | 57 | def StartNative(self, request, context): 58 | """Missing associated documentation comment in .proto file.""" 59 | context.set_code(grpc.StatusCode.UNIMPLEMENTED) 60 | context.set_details('Method not implemented!') 61 | raise NotImplementedError('Method not implemented!') 62 | 63 | def StartEVM(self, request, context): 64 | """Missing associated documentation comment in .proto file.""" 65 | context.set_code(grpc.StatusCode.UNIMPLEMENTED) 66 | context.set_details('Method not implemented!') 67 | raise NotImplementedError('Method not implemented!') 68 | 69 | def Terminate(self, request, context): 70 | """Missing associated documentation comment in .proto file.""" 71 | context.set_code(grpc.StatusCode.UNIMPLEMENTED) 72 | context.set_details('Method not implemented!') 73 | raise NotImplementedError('Method not implemented!') 74 | 75 | def GetStateList(self, request, context): 76 | """Missing associated documentation comment in .proto file.""" 77 | context.set_code(grpc.StatusCode.UNIMPLEMENTED) 78 | context.set_details('Method not implemented!') 79 | raise NotImplementedError('Method not implemented!') 80 | 81 | def GetMessageList(self, request, context): 82 | """Missing associated documentation comment in .proto file.""" 83 | context.set_code(grpc.StatusCode.UNIMPLEMENTED) 84 | context.set_details('Method not implemented!') 85 | raise NotImplementedError('Method not implemented!') 86 | 87 | def CheckManticoreRunning(self, request, context): 88 | """Missing associated documentation comment in .proto file.""" 89 | context.set_code(grpc.StatusCode.UNIMPLEMENTED) 90 | context.set_details('Method not implemented!') 91 | raise NotImplementedError('Method not implemented!') 92 | 93 | def StopServer(self, request, context): 94 | """Missing associated documentation comment in .proto file.""" 95 | context.set_code(grpc.StatusCode.UNIMPLEMENTED) 96 | context.set_details('Method not implemented!') 97 | raise NotImplementedError('Method not implemented!') 98 | 99 | 100 | def add_ManticoreUIServicer_to_server(servicer, server): 101 | rpc_method_handlers = { 102 | 'StartNative': grpc.unary_unary_rpc_method_handler( 103 | servicer.StartNative, 104 | request_deserializer=muicore_dot_MUICore__pb2.NativeArguments.FromString, 105 | response_serializer=muicore_dot_MUICore__pb2.ManticoreInstance.SerializeToString, 106 | ), 107 | 'StartEVM': grpc.unary_unary_rpc_method_handler( 108 | servicer.StartEVM, 109 | request_deserializer=muicore_dot_MUICore__pb2.EVMArguments.FromString, 110 | response_serializer=muicore_dot_MUICore__pb2.ManticoreInstance.SerializeToString, 111 | ), 112 | 'Terminate': grpc.unary_unary_rpc_method_handler( 113 | servicer.Terminate, 114 | request_deserializer=muicore_dot_MUICore__pb2.ManticoreInstance.FromString, 115 | response_serializer=muicore_dot_MUICore__pb2.TerminateResponse.SerializeToString, 116 | ), 117 | 'GetStateList': grpc.unary_unary_rpc_method_handler( 118 | servicer.GetStateList, 119 | request_deserializer=muicore_dot_MUICore__pb2.ManticoreInstance.FromString, 120 | response_serializer=muicore_dot_MUICore__pb2.MUIStateList.SerializeToString, 121 | ), 122 | 'GetMessageList': grpc.unary_unary_rpc_method_handler( 123 | servicer.GetMessageList, 124 | request_deserializer=muicore_dot_MUICore__pb2.ManticoreInstance.FromString, 125 | response_serializer=muicore_dot_MUICore__pb2.MUIMessageList.SerializeToString, 126 | ), 127 | 'CheckManticoreRunning': grpc.unary_unary_rpc_method_handler( 128 | servicer.CheckManticoreRunning, 129 | request_deserializer=muicore_dot_MUICore__pb2.ManticoreInstance.FromString, 130 | response_serializer=muicore_dot_MUICore__pb2.ManticoreRunningStatus.SerializeToString, 131 | ), 132 | 'StopServer': grpc.unary_unary_rpc_method_handler( 133 | servicer.StopServer, 134 | request_deserializer=muicore_dot_MUICore__pb2.StopServerRequest.FromString, 135 | response_serializer=muicore_dot_MUICore__pb2.StopServerResponse.SerializeToString, 136 | ), 137 | } 138 | generic_handler = grpc.method_handlers_generic_handler( 139 | 'muicore.ManticoreUI', rpc_method_handlers) 140 | server.add_generic_rpc_handlers((generic_handler,)) 141 | 142 | 143 | # This class is part of an EXPERIMENTAL API. 144 | class ManticoreUI(object): 145 | """Missing associated documentation comment in .proto file.""" 146 | 147 | @staticmethod 148 | def StartNative(request, 149 | target, 150 | options=(), 151 | channel_credentials=None, 152 | call_credentials=None, 153 | insecure=False, 154 | compression=None, 155 | wait_for_ready=None, 156 | timeout=None, 157 | metadata=None): 158 | return grpc.experimental.unary_unary(request, target, '/muicore.ManticoreUI/StartNative', 159 | muicore_dot_MUICore__pb2.NativeArguments.SerializeToString, 160 | muicore_dot_MUICore__pb2.ManticoreInstance.FromString, 161 | options, channel_credentials, 162 | insecure, call_credentials, compression, wait_for_ready, timeout, metadata) 163 | 164 | @staticmethod 165 | def StartEVM(request, 166 | target, 167 | options=(), 168 | channel_credentials=None, 169 | call_credentials=None, 170 | insecure=False, 171 | compression=None, 172 | wait_for_ready=None, 173 | timeout=None, 174 | metadata=None): 175 | return grpc.experimental.unary_unary(request, target, '/muicore.ManticoreUI/StartEVM', 176 | muicore_dot_MUICore__pb2.EVMArguments.SerializeToString, 177 | muicore_dot_MUICore__pb2.ManticoreInstance.FromString, 178 | options, channel_credentials, 179 | insecure, call_credentials, compression, wait_for_ready, timeout, metadata) 180 | 181 | @staticmethod 182 | def Terminate(request, 183 | target, 184 | options=(), 185 | channel_credentials=None, 186 | call_credentials=None, 187 | insecure=False, 188 | compression=None, 189 | wait_for_ready=None, 190 | timeout=None, 191 | metadata=None): 192 | return grpc.experimental.unary_unary(request, target, '/muicore.ManticoreUI/Terminate', 193 | muicore_dot_MUICore__pb2.ManticoreInstance.SerializeToString, 194 | muicore_dot_MUICore__pb2.TerminateResponse.FromString, 195 | options, channel_credentials, 196 | insecure, call_credentials, compression, wait_for_ready, timeout, metadata) 197 | 198 | @staticmethod 199 | def GetStateList(request, 200 | target, 201 | options=(), 202 | channel_credentials=None, 203 | call_credentials=None, 204 | insecure=False, 205 | compression=None, 206 | wait_for_ready=None, 207 | timeout=None, 208 | metadata=None): 209 | return grpc.experimental.unary_unary(request, target, '/muicore.ManticoreUI/GetStateList', 210 | muicore_dot_MUICore__pb2.ManticoreInstance.SerializeToString, 211 | muicore_dot_MUICore__pb2.MUIStateList.FromString, 212 | options, channel_credentials, 213 | insecure, call_credentials, compression, wait_for_ready, timeout, metadata) 214 | 215 | @staticmethod 216 | def GetMessageList(request, 217 | target, 218 | options=(), 219 | channel_credentials=None, 220 | call_credentials=None, 221 | insecure=False, 222 | compression=None, 223 | wait_for_ready=None, 224 | timeout=None, 225 | metadata=None): 226 | return grpc.experimental.unary_unary(request, target, '/muicore.ManticoreUI/GetMessageList', 227 | muicore_dot_MUICore__pb2.ManticoreInstance.SerializeToString, 228 | muicore_dot_MUICore__pb2.MUIMessageList.FromString, 229 | options, channel_credentials, 230 | insecure, call_credentials, compression, wait_for_ready, timeout, metadata) 231 | 232 | @staticmethod 233 | def CheckManticoreRunning(request, 234 | target, 235 | options=(), 236 | channel_credentials=None, 237 | call_credentials=None, 238 | insecure=False, 239 | compression=None, 240 | wait_for_ready=None, 241 | timeout=None, 242 | metadata=None): 243 | return grpc.experimental.unary_unary(request, target, '/muicore.ManticoreUI/CheckManticoreRunning', 244 | muicore_dot_MUICore__pb2.ManticoreInstance.SerializeToString, 245 | muicore_dot_MUICore__pb2.ManticoreRunningStatus.FromString, 246 | options, channel_credentials, 247 | insecure, call_credentials, compression, wait_for_ready, timeout, metadata) 248 | 249 | @staticmethod 250 | def StopServer(request, 251 | target, 252 | options=(), 253 | channel_credentials=None, 254 | call_credentials=None, 255 | insecure=False, 256 | compression=None, 257 | wait_for_ready=None, 258 | timeout=None, 259 | metadata=None): 260 | return grpc.experimental.unary_unary(request, target, '/muicore.ManticoreUI/StopServer', 261 | muicore_dot_MUICore__pb2.StopServerRequest.SerializeToString, 262 | muicore_dot_MUICore__pb2.StopServerResponse.FromString, 263 | options, channel_credentials, 264 | insecure, call_credentials, compression, wait_for_ready, timeout, metadata) 265 | -------------------------------------------------------------------------------- /MUICore/muicore/__init__.py: -------------------------------------------------------------------------------- 1 | from . import mui_server 2 | -------------------------------------------------------------------------------- /MUICore/muicore/evm_utils.py: -------------------------------------------------------------------------------- 1 | import argparse 2 | import shlex 3 | from typing import List, Type 4 | 5 | from crytic_compile import cryticparser 6 | from manticore.core.plugin import Profiler 7 | from manticore.ethereum import ( 8 | DetectDelegatecall, 9 | DetectEnvInstruction, 10 | DetectExternalCallAndLeak, 11 | DetectIntegerOverflow, 12 | DetectInvalid, 13 | DetectManipulableBalance, 14 | Detector, 15 | DetectReentrancyAdvanced, 16 | DetectReentrancySimple, 17 | DetectSuicidal, 18 | DetectUninitializedMemory, 19 | DetectUninitializedStorage, 20 | DetectUnusedRetVal, 21 | ManticoreEVM, 22 | ) 23 | from manticore.ethereum.plugins import ( 24 | FilterFunctions, 25 | KeepOnlyIfStorageChanges, 26 | LoopDepthLimiter, 27 | SkipRevertBasicBlocks, 28 | VerboseTrace, 29 | ) 30 | from manticore.utils import config 31 | 32 | 33 | def get_detectors_classes() -> List[Type[Detector]]: 34 | return [ 35 | DetectInvalid, 36 | DetectIntegerOverflow, 37 | DetectUninitializedStorage, 38 | DetectUninitializedMemory, 39 | DetectReentrancySimple, 40 | DetectReentrancyAdvanced, 41 | DetectUnusedRetVal, 42 | DetectSuicidal, 43 | DetectDelegatecall, 44 | DetectExternalCallAndLeak, 45 | DetectEnvInstruction, 46 | DetectManipulableBalance, 47 | # The RaceCondition detector has been disabled for now as it seems to collide with IntegerOverflow detector 48 | # DetectRaceCondition 49 | ] 50 | 51 | 52 | def parse_detectors(detectors_to_exclude: List[str]) -> List[Type[Detector]]: 53 | """returns a list of detectors that should be used""" 54 | all_detector_classes = get_detectors_classes() 55 | all_detector_args = map(lambda x: x.ARGUMENT, all_detector_classes) 56 | for d in detectors_to_exclude: 57 | if d not in all_detector_args: 58 | raise Exception( 59 | f"{d} is not a detector name, must be one of {list(all_detector_args)}. See also `--list-detectors`." 60 | ) 61 | 62 | return [d for d in all_detector_classes if d.ARGUMENT not in detectors_to_exclude] 63 | 64 | 65 | def setup_detectors_flags( 66 | detectors_to_exclude: List[str], additional_flags: str, m: ManticoreEVM 67 | ) -> argparse.Namespace: 68 | """parse and apply additional arguments for manticore EVM execution, CLI-style""" 69 | 70 | parser = argparse.ArgumentParser( 71 | description="Symbolic execution tool", 72 | prog="manticore", 73 | formatter_class=argparse.ArgumentDefaultsHelpFormatter, 74 | ) 75 | 76 | consts = config.get_group("main") 77 | consts.add("profile", default=False, description="Enable worker profiling mode") 78 | consts.add( 79 | "explore_balance", 80 | default=False, 81 | description="Explore states in which only the balance was changed", 82 | ) 83 | 84 | consts.add( 85 | "skip_reverts", 86 | default=False, 87 | description="Simply avoid exploring basic blocks that end in a REVERT", 88 | ) 89 | 90 | # Add crytic compile arguments 91 | # See https://github.com/crytic/crytic-compile/wiki/Configuration 92 | cryticparser.init(parser) 93 | 94 | eth_flags = parser.add_argument_group("Ethereum flags") 95 | 96 | eth_flags.add_argument( 97 | "--verbose-trace", 98 | action="store_true", 99 | help="Dump an extra verbose trace for each state", 100 | ) 101 | 102 | eth_flags.add_argument( 103 | "--txnocoverage", 104 | action="store_true", 105 | help="Do not use coverage as stopping criteria", 106 | ) 107 | 108 | eth_flags.add_argument( 109 | "--txnoether", 110 | action="store_true", 111 | help="Do not attempt to send ether to contract", 112 | ) 113 | 114 | eth_flags.add_argument( 115 | "--txpreconstrain", 116 | action="store_true", 117 | help="Constrain human transactions to avoid exceptions in the contract function dispatcher", 118 | ) 119 | 120 | eth_flags.add_argument( 121 | "--avoid-constant", 122 | action="store_true", 123 | help="Avoid exploring constant functions for human transactions", 124 | ) 125 | 126 | eth_flags.add_argument( 127 | "--limit-loops", 128 | action="store_true", 129 | help="Limit loops depth", 130 | ) 131 | 132 | eth_flags.add_argument( 133 | "--no-testcases", 134 | action="store_true", 135 | help="Do not generate testcases for discovered states when analysis finishes", 136 | ) 137 | 138 | eth_flags.add_argument( 139 | "--only-alive-testcases", 140 | action="store_true", 141 | help="Do not generate testcases for invalid/throwing states when analysis finishes", 142 | ) 143 | 144 | eth_flags.add_argument( 145 | "--thorough-mode", 146 | action="store_true", 147 | help="Configure Manticore for more exhaustive exploration. Evaluate gas, generate testcases for dead states, " 148 | "explore constant functions, and run a small suite of detectors.", 149 | ) 150 | 151 | config_flags = parser.add_argument_group("Constants") 152 | config.add_config_vars_to_argparse(config_flags) 153 | 154 | args = parser.parse_args(shlex.split(additional_flags)) 155 | config.process_config_values(parser, args) 156 | 157 | if not args.thorough_mode: 158 | args.avoid_constant = True 159 | args.exclude_all = True 160 | args.only_alive_testcases = True 161 | consts_evm = config.get_group("evm") 162 | consts_evm.oog = "ignore" 163 | consts.skip_reverts = True 164 | 165 | if consts.skip_reverts: 166 | m.register_plugin(SkipRevertBasicBlocks()) 167 | 168 | if consts.explore_balance: 169 | m.register_plugin(KeepOnlyIfStorageChanges()) 170 | 171 | if args.verbose_trace: 172 | m.register_plugin(VerboseTrace()) 173 | 174 | if args.limit_loops: 175 | m.register_plugin(LoopDepthLimiter()) 176 | 177 | for detector in parse_detectors(detectors_to_exclude): 178 | m.register_plugin(detector()) 179 | 180 | if consts.profile: 181 | profiler = Profiler() 182 | m.register_plugin(profiler) 183 | 184 | if args.avoid_constant: 185 | # avoid all human level tx that has no effect on the storage 186 | filter_nohuman_constants = FilterFunctions( 187 | regexp=r".*", depth="human", mutability="constant", include=False 188 | ) 189 | m.register_plugin(filter_nohuman_constants) 190 | 191 | if m.plugins: 192 | print(f'Registered plugins: {", ".join(d.name for d in m.plugins.values())}') 193 | 194 | return args 195 | -------------------------------------------------------------------------------- /MUICore/muicore/introspect_plugin.py: -------------------------------------------------------------------------------- 1 | from datetime import datetime 2 | 3 | from manticore.core.plugin import IntrospectionAPIPlugin, StateDescriptor 4 | from manticore.core.state import StateBase 5 | from manticore.utils.enums import StateLists 6 | 7 | 8 | # from https://github.com/trailofbits/ManticoreUI/blob/master/mui/introspect_plugin.py 9 | class MUIIntrospectionPlugin(IntrospectionAPIPlugin): 10 | NAME = "MUIIntrospectionPlugin" 11 | 12 | @property 13 | def name(self) -> str: 14 | return "MUIIntrospectionPlugin" 15 | 16 | def create_state(self, state_id: int): 17 | """Override create_state to force a state update right after creation. 18 | This is helpful when retrieving info from a state yet to execute.""" 19 | super().create_state(state_id) 20 | state = self.manticore._load(state_id) 21 | self._force_update_state_descriptor(state) 22 | 23 | def will_fork_state_callback(self, state: StateBase, expression, solutions, policy): 24 | self._force_update_state_descriptor(state) 25 | 26 | def will_transition_state_callback( 27 | self, state_id: int, from_list: StateLists, to_list: StateLists 28 | ): 29 | state = self.manticore._load(state_id) 30 | self._force_update_state_descriptor(state) 31 | 32 | def _force_update_state_descriptor(self, state: StateBase): 33 | """Force a given state to update its information, which can include the current PC, etc. 34 | Calling _update_state_descriptor directly may become an issue if specific state implementations 35 | start to require additional arguments for this method.""" 36 | with self.locked_context("manticore_state", dict) as context: 37 | state._update_state_descriptor( 38 | context.setdefault(state.id, StateDescriptor(state_id=state.id)), 39 | ) 40 | context[state.id].last_intermittent_update = datetime.now() 41 | 42 | def did_terminate_worker_callback(self, worker_id: int): 43 | print(f"worker exits (id: {worker_id})") 44 | -------------------------------------------------------------------------------- /MUICore/muicore/mui_server.py: -------------------------------------------------------------------------------- 1 | import argparse 2 | import dataclasses 3 | import logging 4 | import shutil 5 | import time 6 | import uuid 7 | from concurrent import futures 8 | from pathlib import Path 9 | from threading import Event, Thread 10 | from typing import Any, Callable, Dict, Optional, Set 11 | 12 | import grpc 13 | from grpc._server import _Context 14 | from manticore.core.plugin import ( 15 | InstructionCounter, 16 | RecordSymbolicBranches, 17 | StateDescriptor, 18 | Tracer, 19 | Visited, 20 | ) 21 | from manticore.core.state import StateBase 22 | from manticore.core.worker import WorkerProcess, WorkerThread 23 | from manticore.ethereum import ManticoreEVM 24 | from manticore.native import Manticore 25 | from manticore.utils.enums import StateLists, StateStatus 26 | from manticore.utils.helpers import deque 27 | from manticore.utils.log import CallbackStream, ManticoreContextFilter 28 | 29 | from .evm_utils import setup_detectors_flags 30 | from .introspect_plugin import MUIIntrospectionPlugin 31 | from .MUICore_pb2 import * 32 | from .MUICore_pb2_grpc import ManticoreUIServicer, add_ManticoreUIServicer_to_server 33 | from .native_utils import parse_native_arguments 34 | 35 | 36 | class ManticoreWrapper: 37 | def __init__( 38 | self, mcore_object: Manticore, thread_target: Callable, *thread_args: Any 39 | ): 40 | self.uuid: str = uuid.uuid4().hex 41 | self.manticore_object: Manticore = mcore_object 42 | # mimics Manticore repository, reasoning for the queue size difference is provided in Manticore: 43 | # https://github.com/trailofbits/manticore/blob/5a258f499098394c0af25e2e3f00b1b603c2334d/manticore/core/manticore.py#L133-L135 44 | self.log_queue = ( 45 | mcore_object._manager.Queue(15000) 46 | if mcore_object._worker_type == WorkerProcess 47 | else deque(maxlen=5000) 48 | ) 49 | # saves a copy of all state descriptors after analysis is complete or terminated 50 | # but before the finalize() operation which destroys all states 51 | self.final_states: Optional[Dict[int, StateDescriptor]] = None 52 | 53 | self.thread: Thread = Thread( 54 | target=thread_target, 55 | args=(self,) + thread_args, 56 | daemon=True, 57 | name=self.uuid, 58 | ) 59 | self.thread.start() 60 | 61 | def append_log(self, msg: str) -> None: 62 | q = self.log_queue 63 | try: 64 | q.append(msg) 65 | except AttributeError: 66 | # mimics Manticore repository, reasoning for using append and catching an AttributeError is provided in Manticore: 67 | # https://github.com/trailofbits/manticore/blob/5a258f499098394c0af25e2e3f00b1b603c2334d/manticore/core/worker.py#L297-L303 68 | if q.full(): 69 | q.get() 70 | q.put(msg) 71 | 72 | 73 | class MUIServicer(ManticoreUIServicer): 74 | """Provides functionality for the methods set out in the protobuf spec""" 75 | 76 | def __init__(self, stop_event: Event): 77 | """Initializes the dict that keeps track of all created manticore instances, as well as avoid/find address set""" 78 | 79 | self.manticore_instances: Dict[str, ManticoreWrapper] = {} 80 | self.stop_event: Event = stop_event 81 | 82 | manticore_logger = logging.getLogger("manticore") 83 | manticore_logger.parent = None 84 | manticore_logger.setLevel(logging.WARNING) 85 | 86 | custom_log_handler = logging.StreamHandler(CallbackStream(self.log_callback)) 87 | custom_log_handler.setFormatter( 88 | logging.Formatter( 89 | "%(threadName)s %(asctime)s: [%(process)d] %(name)s:%(levelname)s %(message)s" 90 | ) 91 | ) 92 | custom_log_handler.addFilter(ManticoreContextFilter()) 93 | 94 | manticore_logger.addHandler(custom_log_handler) 95 | 96 | def log_callback(self, msg: str): 97 | print(msg, end="") 98 | msg_split = msg.split() 99 | thread_name = msg_split[0] 100 | msg_content = " ".join(msg_split[1:]) 101 | 102 | if thread_name in self.manticore_instances: 103 | # This will always be True if multiprocessing or single is used, since all WorkerProcess/WorkerSingle 104 | # instances will share the same Thread name as the ManticoreWrapper's mthread which is added on Start 105 | self.manticore_instances[thread_name].append_log(msg_content) 106 | else: 107 | for mwrapper in filter( 108 | lambda x: x.manticore_object._worker_type == WorkerThread, 109 | list(self.manticore_instances.values())[::-1], 110 | ): # Thread name/idents can be reused, so start search from most recently-started instance 111 | for worker in mwrapper.manticore_object._workers + list( 112 | mwrapper.manticore_object._daemon_threads.values() 113 | ): 114 | if type(worker._t) == Thread and worker._t.name == thread_name: 115 | worker._t.name = mwrapper.uuid 116 | mwrapper.append_log(msg_content) 117 | return 118 | 119 | def StartNative( 120 | self, native_arguments: NativeArguments, context: _Context 121 | ) -> ManticoreInstance: 122 | """Starts a singular Manticore instance to analyze a native binary""" 123 | try: 124 | parsed = parse_native_arguments(native_arguments.additional_mcore_args) 125 | except Exception as e: 126 | print(e) 127 | context.set_code(grpc.StatusCode.INVALID_ARGUMENT) 128 | context.set_details("Additional arguments could not be parsed!") 129 | return ManticoreInstance() 130 | try: 131 | m = Manticore.linux( 132 | native_arguments.program_path, 133 | argv=None 134 | if not native_arguments.binary_args 135 | else list(native_arguments.binary_args), 136 | envp=None 137 | if not native_arguments.envp 138 | else { 139 | key: val 140 | for key, val in [e.split("=") for e in native_arguments.envp] 141 | }, 142 | symbolic_files=None 143 | if not native_arguments.symbolic_files 144 | else list(native_arguments.symbolic_files), 145 | concrete_start="" 146 | if not native_arguments.concrete_start 147 | else native_arguments.concrete_start, 148 | stdin_size=265 149 | if not native_arguments.stdin_size 150 | else int(native_arguments.stdin_size), 151 | workspace_url=parsed.workspace, 152 | introspection_plugin_type=MUIIntrospectionPlugin, 153 | ) 154 | except Exception as e: 155 | print(e) 156 | context.set_code(grpc.StatusCode.INVALID_ARGUMENT) 157 | context.set_details("Basic arguments are invalid!") 158 | return ManticoreInstance() 159 | 160 | try: 161 | m.register_plugin(InstructionCounter()) 162 | m.register_plugin(Visited(parsed.coverage)) 163 | m.register_plugin(Tracer()) 164 | m.register_plugin(RecordSymbolicBranches()) 165 | 166 | if parsed.names is not None: 167 | m.apply_model_hooks(parsed.names) 168 | 169 | if parsed.assertions: 170 | m.load_assertions(parsed.assertions) 171 | 172 | def find_f(state: StateBase): 173 | bufs = state.solve_one_n_batched(state.input_symbols) 174 | for symbol, buf in zip(state.input_symbols, bufs): 175 | print(f"{symbol.name}: {buf!r}\n") 176 | with m.locked_context() as context: 177 | m.kill() 178 | state.abandon() 179 | 180 | def avoid_f(state: StateBase): 181 | state.abandon() 182 | 183 | for hook in native_arguments.hooks: 184 | if hook.type == Hook.HookType.FIND: 185 | m.add_hook(hook.address, find_f) 186 | elif hook.type == Hook.HookType.AVOID: 187 | m.add_hook(hook.address, avoid_f) 188 | elif hook.type == Hook.HookType.CUSTOM: 189 | exec(hook.func_text, {"addr": hook.address, "m": m}) 190 | elif hook.type == Hook.HookType.GLOBAL: 191 | exec(hook.func_text, {"m": m}) 192 | 193 | except Exception as e: 194 | print(e) 195 | context.set_code(grpc.StatusCode.INVALID_ARGUMENT) 196 | context.set_details("Hooks set are invalid!") 197 | return ManticoreInstance() 198 | 199 | try: 200 | 201 | def manticore_native_runner(mcore_wrapper: ManticoreWrapper): 202 | mcore_wrapper.manticore_object.run() 203 | mcore_wrapper.final_states = { 204 | k: dataclasses.replace(v) 205 | for k, v in mcore_wrapper.manticore_object.introspect().items() 206 | } 207 | mcore_wrapper.manticore_object.finalize() 208 | 209 | manticore_wrapper = ManticoreWrapper(m, manticore_native_runner) 210 | self.manticore_instances[manticore_wrapper.uuid] = manticore_wrapper 211 | 212 | except Exception as e: 213 | print(e) 214 | context.set_code(grpc.StatusCode.INTERNAL) 215 | context.set_details( 216 | "Manticore failed to start or crashed during execution!" 217 | ) 218 | return ManticoreInstance() 219 | 220 | return ManticoreInstance(uuid=manticore_wrapper.uuid) 221 | 222 | def StartEVM( 223 | self, evm_arguments: EVMArguments, context: _Context 224 | ) -> ManticoreInstance: 225 | """Starts a singular Manticore instance to analyze a solidity contract""" 226 | if evm_arguments.contract_path == "": 227 | context.set_code(grpc.StatusCode.INVALID_ARGUMENT) 228 | context.set_details("Contract path not specified!") 229 | return ManticoreInstance() 230 | if not Path(evm_arguments.contract_path).is_file(): 231 | context.set_code(grpc.StatusCode.INVALID_ARGUMENT) 232 | context.set_details( 233 | f"Contract path invalid: '{evm_arguments.contract_path}'" 234 | ) 235 | return ManticoreInstance() 236 | 237 | if evm_arguments.solc_bin: 238 | solc_bin_path = evm_arguments.solc_bin 239 | elif shutil.which("solc"): 240 | solc_bin_path = str(shutil.which("solc")) 241 | else: 242 | context.set_code(grpc.StatusCode.INVALID_ARGUMENT) 243 | context.set_details( 244 | f"solc binary neither specified in EVMArguments nor found in PATH!" 245 | ) 246 | return ManticoreInstance() 247 | 248 | try: 249 | m = ManticoreEVM() 250 | 251 | args = setup_detectors_flags( 252 | list(evm_arguments.detectors_to_exclude), 253 | evm_arguments.additional_flags, 254 | m, 255 | ) 256 | 257 | def manticore_evm_runner( 258 | mcore_wrapper: ManticoreWrapper, args: argparse.Namespace 259 | ): 260 | 261 | mcore_wrapper.manticore_object.multi_tx_analysis( 262 | evm_arguments.contract_path, 263 | contract_name=evm_arguments.contract_name, 264 | tx_limit=-1 265 | if not evm_arguments.tx_limit 266 | else evm_arguments.tx_limit, 267 | tx_use_coverage=True 268 | if args.txnocoverage == None 269 | else args.txnocoverage, 270 | tx_send_ether=True if args.txnoether == None else args.txnoether, 271 | tx_account="attacker" 272 | if not evm_arguments.tx_account 273 | else evm_arguments.tx_account, 274 | tx_preconstrain=False 275 | if args.txpreconstrain == None 276 | else args.txpreconstrain, 277 | compile_args={"solc_solcs_bin": solc_bin_path}, 278 | ) 279 | 280 | mcore_wrapper.final_states = { 281 | k: dataclasses.replace(v) 282 | for k, v in mcore_wrapper.manticore_object.introspect().items() 283 | } 284 | 285 | if not args.no_testcases: 286 | mcore_wrapper.manticore_object.finalize( 287 | only_alive_states=args.only_alive_testcases 288 | ) 289 | else: 290 | mcore_wrapper.manticore_object.kill() 291 | 292 | manticore_wrapper = ManticoreWrapper(m, manticore_evm_runner, args) 293 | self.manticore_instances[manticore_wrapper.uuid] = manticore_wrapper 294 | 295 | except Exception as e: 296 | print(e) 297 | context.set_code(grpc.StatusCode.INTERNAL) 298 | context.set_details( 299 | "Manticore failed to start or crashed during execution!" 300 | ) 301 | return ManticoreInstance() 302 | 303 | return ManticoreInstance(uuid=manticore_wrapper.uuid) 304 | 305 | def Terminate( 306 | self, mcore_instance: ManticoreInstance, context: _Context 307 | ) -> TerminateResponse: 308 | """Terminates the specified Manticore instance.""" 309 | if mcore_instance.uuid not in self.manticore_instances: 310 | context.set_code(grpc.StatusCode.FAILED_PRECONDITION) 311 | context.set_details("Specified Manticore instance not found!") 312 | return TerminateResponse() 313 | 314 | m_wrapper = self.manticore_instances[mcore_instance.uuid] 315 | 316 | if not ( 317 | m_wrapper.manticore_object.is_running() and m_wrapper.thread.is_alive() 318 | ): 319 | return TerminateResponse() 320 | m_wrapper.manticore_object.kill() 321 | return TerminateResponse() 322 | 323 | def GetStateList( 324 | self, mcore_instance: ManticoreInstance, context: _Context 325 | ) -> MUIStateList: 326 | """Returns full list of states for given ManticoreInstance. 327 | Currently, implementation is based on MUI's Binary Ninja plugin.""" 328 | active_states = [] 329 | waiting_states = [] 330 | forked_states = [] 331 | errored_states = [] 332 | complete_states = [] 333 | if mcore_instance.uuid not in self.manticore_instances: 334 | context.set_code(grpc.StatusCode.FAILED_PRECONDITION) 335 | context.set_details("Specified Manticore instance not found!") 336 | return MUIStateList() 337 | 338 | mcore_wrapper = self.manticore_instances[mcore_instance.uuid] 339 | states = ( 340 | mcore_wrapper.final_states 341 | if mcore_wrapper.final_states is not None 342 | else mcore_wrapper.manticore_object.introspect() 343 | ) 344 | 345 | for state_id, state_desc in states.items(): 346 | 347 | state_args = {"state_id": state_id} 348 | 349 | if isinstance(state_desc.pc, int): 350 | state_args["pc"] = state_desc.pc 351 | elif isinstance(state_desc.last_pc, int): 352 | state_args["pc"] = state_desc.last_pc 353 | 354 | if isinstance(state_desc.parent, int): 355 | state_args["parent_id"] = state_desc.parent 356 | else: 357 | state_args["parent_id"] = -1 358 | 359 | state_args["children_ids"] = list(state_desc.children) 360 | 361 | s = MUIState(**state_args) 362 | 363 | if state_desc.status == StateStatus.running: 364 | active_states.append(s) 365 | elif state_desc.status in ( 366 | StateStatus.waiting_for_solver, 367 | StateStatus.waiting_for_worker, 368 | ): 369 | waiting_states.append(s) 370 | elif state_desc.status == StateStatus.destroyed: 371 | forked_states.append(s) 372 | elif state_desc.status == StateStatus.stopped: 373 | if state_desc.state_list == StateLists.killed: 374 | errored_states.append(s) 375 | else: 376 | complete_states.append(s) 377 | else: 378 | raise ValueError(f"Unknown status {state_desc.status}") 379 | 380 | return MUIStateList( 381 | active_states=active_states, 382 | waiting_states=waiting_states, 383 | forked_states=forked_states, 384 | errored_states=errored_states, 385 | complete_states=complete_states, 386 | ) 387 | 388 | def GetMessageList( 389 | self, mcore_instance: ManticoreInstance, context: _Context 390 | ) -> MUIMessageList: 391 | """Returns any new log messages for given ManticoreInstance since the previous call. 392 | Currently, implementation is based on TUI.""" 393 | if mcore_instance.uuid not in self.manticore_instances: 394 | context.set_code(grpc.StatusCode.FAILED_PRECONDITION) 395 | context.set_details("Specified Manticore instance not found!") 396 | return MUIMessageList() 397 | 398 | q = self.manticore_instances[mcore_instance.uuid].log_queue 399 | i = 0 400 | messages = [] 401 | while not q.empty(): 402 | msg = MUILogMessage(content=q.get()) 403 | messages.append(msg) 404 | i += 1 405 | return MUIMessageList(messages=messages) 406 | 407 | def CheckManticoreRunning( 408 | self, mcore_instance: ManticoreInstance, context: _Context 409 | ) -> ManticoreRunningStatus: 410 | 411 | if mcore_instance.uuid not in self.manticore_instances: 412 | context.set_code(grpc.StatusCode.FAILED_PRECONDITION) 413 | context.set_details("Specified Manticore instance not found!") 414 | return ManticoreRunningStatus() 415 | 416 | m_wrapper = self.manticore_instances[mcore_instance.uuid] 417 | 418 | return ManticoreRunningStatus(is_running=(m_wrapper.thread.is_alive())) 419 | 420 | def StopServer( 421 | self, request: StopServerRequest, context: _Context 422 | ) -> StopServerResponse: 423 | to_warn = False 424 | for mwrapper in self.manticore_instances.values(): 425 | mwrapper.manticore_object.kill() 426 | stime = time.time() 427 | while mwrapper.thread.is_alive(): 428 | time.sleep(1) 429 | if (time.time() - stime) > 10: 430 | to_warn = True 431 | break 432 | 433 | if to_warn: 434 | warning_message = "WARNING: Not all Manticore processes were shut down successfully before timeout. There may be extra processes running even after the server has stopped." 435 | context.set_code(grpc.StatusCode.INTERNAL) 436 | context.set_details(warning_message) 437 | print(warning_message) 438 | 439 | self.stop_event.set() 440 | return StopServerResponse() 441 | 442 | 443 | def main(): 444 | stop_event = Event() 445 | server = grpc.server(futures.ThreadPoolExecutor(max_workers=10)) 446 | add_ManticoreUIServicer_to_server(MUIServicer(stop_event), server) 447 | server.add_insecure_port("[::]:50010") 448 | server.start() 449 | stop_event.wait() 450 | server.stop(None) 451 | print("shutdown gracefully!") 452 | 453 | 454 | if __name__ == "__main__": 455 | main() 456 | -------------------------------------------------------------------------------- /MUICore/muicore/native_utils.py: -------------------------------------------------------------------------------- 1 | import argparse 2 | import shlex 3 | 4 | import pkg_resources 5 | from manticore.utils import config 6 | from manticore.utils.log import set_verbosity 7 | 8 | 9 | def parse_native_arguments(additional_args: str) -> argparse.Namespace: 10 | """parse additional arguments for manticore native execution, CLI-style""" 11 | 12 | def positive(value): 13 | ivalue = int(value) 14 | if ivalue <= 0: 15 | raise argparse.ArgumentTypeError("Argument must be positive") 16 | return ivalue 17 | 18 | parser = argparse.ArgumentParser( 19 | description="Symbolic execution tool", 20 | prog="manticore", 21 | formatter_class=argparse.ArgumentDefaultsHelpFormatter, 22 | ) 23 | 24 | parser.add_argument("--context", type=str, default=None, help=argparse.SUPPRESS) 25 | parser.add_argument( 26 | "--coverage", 27 | type=str, 28 | default="visited.txt", 29 | help="Where to write the coverage data", 30 | ) 31 | parser.add_argument("--names", type=str, default=None, help=argparse.SUPPRESS) 32 | parser.add_argument( 33 | "--no-colors", 34 | action="store_true", 35 | help="Disable ANSI color escape sequences in output", 36 | ) 37 | parser.add_argument("--offset", type=int, default=16, help=argparse.SUPPRESS) 38 | # FIXME (theo) Add some documentation on the different search policy options 39 | parser.add_argument( 40 | "--policy", 41 | type=str, 42 | default="random", 43 | help=( 44 | "Search policy. random|adhoc|uncovered|dicount" 45 | "|icount|syscount|depth. (use + (max) or - (min)" 46 | " to specify order. e.g. +random)" 47 | ), 48 | ) 49 | parser.add_argument( 50 | "-v", action="count", default=1, help="Specify verbosity level from -v to -vvvv" 51 | ) 52 | parser.add_argument( 53 | "--workspace", 54 | type=str, 55 | default=None, 56 | help=("A folder name for temporaries and results." "(default mcore_?????)"), 57 | ) 58 | 59 | current_version = pkg_resources.get_distribution("manticore").version 60 | parser.add_argument( 61 | "--version", 62 | action="version", 63 | version=f"Manticore {current_version}", 64 | help="Show program version information", 65 | ) 66 | parser.add_argument( 67 | "--config", 68 | type=str, 69 | help="Manticore config file (.yml) to use. (default config file pattern is: ./[.]m[anti]core.yml)", 70 | ) 71 | 72 | bin_flags = parser.add_argument_group("Binary flags") 73 | bin_flags.add_argument( 74 | "--entrysymbol", type=str, default=None, help="Symbol as entry point" 75 | ) 76 | bin_flags.add_argument( 77 | "--assertions", type=str, default=None, help=argparse.SUPPRESS 78 | ) 79 | bin_flags.add_argument("--buffer", type=str, help=argparse.SUPPRESS) 80 | bin_flags.add_argument( 81 | "--data", 82 | type=str, 83 | default="", 84 | help="Initial concrete concrete_data for the input symbolic buffer", 85 | ) 86 | bin_flags.add_argument( 87 | "--file", 88 | type=str, 89 | default=[], 90 | action="append", 91 | dest="files", 92 | help="Specify symbolic input file, '+' marks symbolic bytes", 93 | ) 94 | bin_flags.add_argument( 95 | "--env", 96 | type=str, 97 | nargs=1, 98 | default=[], 99 | action="append", 100 | help='Add an environment variable. Use "+" for symbolic bytes. (VARNAME=++++)', 101 | ) 102 | bin_flags.add_argument( 103 | "--pure-symbolic", 104 | action="store_true", 105 | help="Treat all writable memory as symbolic", 106 | ) 107 | 108 | config_flags = parser.add_argument_group("Constants") 109 | config.add_config_vars_to_argparse(config_flags) 110 | 111 | parsed = parser.parse_args(shlex.split(additional_args)) 112 | config.process_config_values(parser, parsed) 113 | 114 | if parsed.policy.startswith("min"): 115 | parsed.policy = "-" + parsed.policy[3:] 116 | elif parsed.policy.startswith("max"): 117 | parsed.policy = "+" + parsed.policy[3:] 118 | 119 | set_verbosity(parsed.v) 120 | 121 | return parsed 122 | -------------------------------------------------------------------------------- /MUICore/pyproject.toml: -------------------------------------------------------------------------------- 1 | [tool.mypy] 2 | files = ["muicore", "tests"] 3 | ignore_missing_imports = true 4 | warn_redundant_casts = true 5 | warn_unused_ignores = true 6 | warn_unreachable = true 7 | 8 | [tool.isort] 9 | profile = "black" 10 | skip = ["muicore/MUICore_pb2.py", "muicore/MUICore_pb2_grpc.py"] 11 | 12 | [tool.black] 13 | line-length = 88 14 | extend-exclude = ''' 15 | MUICore_pb2_grpc\.py 16 | | MUICore_pb2\.py 17 | | MUICore_pb2\.pyi 18 | ''' 19 | -------------------------------------------------------------------------------- /MUICore/requirements-dev.txt: -------------------------------------------------------------------------------- 1 | black==22.3.0 2 | isort==5.10.1 3 | mypy==0.942 4 | types-setuptools==57.4.12 5 | mypy-protobuf==3.2.0 6 | grpcio==1.46.3 7 | grpcio-tools==1.46.3 8 | -------------------------------------------------------------------------------- /MUICore/setup.py: -------------------------------------------------------------------------------- 1 | from setuptools import Command, find_packages, setup 2 | 3 | 4 | class GenerateCommand(Command): 5 | description = ( 6 | "generates muicore server protobuf + grpc code from protobuf specification file" 7 | ) 8 | user_options = [] 9 | 10 | def initialize_options(self): 11 | pass 12 | 13 | def finalize_options(self): 14 | pass 15 | 16 | def run(self): 17 | from grpc.tools import protoc 18 | 19 | protoc.main( 20 | [ 21 | "grpc_tools.protoc", 22 | "-I.", 23 | "--python_out=.", 24 | "--grpc_python_out=.", 25 | "--mypy_out=.", 26 | "./muicore/MUICore.proto", 27 | ] 28 | ) 29 | 30 | 31 | native_deps = [ 32 | "capstone==5.0.0rc2", 33 | "pyelftools", 34 | "unicorn==1.0.2", 35 | ] 36 | 37 | setup( 38 | name="muicore", 39 | version="0.0.1", 40 | packages=find_packages(exclude=["tests", "tests.*"]), 41 | install_requires=[ 42 | # manticore from upstream chess branch with fixes not yet in master 43 | "manticore @ git+https://github.com/trailofbits/manticore.git@634b6a4cdc295c93027b1dbe5037e574cf76200b", 44 | "protobuf==3.20.1", 45 | "grpcio==1.46.3", 46 | "crytic-compile==0.2.2", 47 | ] 48 | + native_deps, 49 | extras_require={"dev": ["grpcio-tools"]}, 50 | entry_points={ 51 | "console_scripts": [ 52 | "muicore=muicore.mui_server:main", 53 | ], 54 | "distutils.commands": ["generate = GenerateCommand"], 55 | }, 56 | cmdclass={ 57 | "generate": GenerateCommand, 58 | }, 59 | ) 60 | -------------------------------------------------------------------------------- /MUICore/tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/trailofbits/ManticoreUI-Ghidra/8ba2fa35c131c178512d61e52d6929ed74e1e220/MUICore/tests/__init__.py -------------------------------------------------------------------------------- /MUICore/tests/binaries/arguments_linux_amd64: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/trailofbits/ManticoreUI-Ghidra/8ba2fa35c131c178512d61e52d6929ed74e1e220/MUICore/tests/binaries/arguments_linux_amd64 -------------------------------------------------------------------------------- /MUICore/tests/contracts/adder.sol: -------------------------------------------------------------------------------- 1 | contract Adder { 2 | function incremented(uint value) public returns (uint){ 3 | if (value == 1) 4 | revert(); 5 | return value + 1; 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /MUICore/tests/mock_classes.py: -------------------------------------------------------------------------------- 1 | import grpc 2 | from grpc._server import _Context 3 | 4 | 5 | class MockContext(_Context): 6 | def __init__(self) -> None: 7 | self.code: grpc.StatusCode = grpc.StatusCode.OK 8 | self.details: str = "" 9 | 10 | def set_details(self, details: str) -> None: 11 | self.details = details 12 | 13 | def set_code(self, code: grpc.StatusCode) -> None: 14 | self.code = code 15 | 16 | def reset(self): 17 | self.code: grpc.StatusCode = grpc.StatusCode.OK 18 | self.details: str = "" 19 | -------------------------------------------------------------------------------- /MUICore/tests/solc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/trailofbits/ManticoreUI-Ghidra/8ba2fa35c131c178512d61e52d6929ed74e1e220/MUICore/tests/solc -------------------------------------------------------------------------------- /MUICore/tests/test_ethereum.py: -------------------------------------------------------------------------------- 1 | import glob 2 | import os 3 | import threading 4 | import time 5 | import unittest 6 | import unittest.mock 7 | from inspect import currentframe, getframeinfo 8 | from pathlib import Path 9 | from shutil import rmtree, which 10 | from uuid import UUID, uuid4 11 | 12 | import grpc 13 | 14 | from muicore import mui_server 15 | from muicore.MUICore_pb2 import * 16 | from tests.mock_classes import MockContext 17 | 18 | 19 | class MUICoreEVMTest(unittest.TestCase): 20 | def setUp(self): 21 | self.dirname = str(Path(getframeinfo(currentframe()).filename).resolve().parent) 22 | self.contract_path = str(self.dirname / Path("contracts") / Path("adder.sol")) 23 | self.test_event = threading.Event() 24 | self.servicer = mui_server.MUIServicer(self.test_event) 25 | self.solc_path = str(self.dirname / Path("solc")) 26 | self.context = MockContext() 27 | 28 | def tearDown(self): 29 | for mwrapper in self.servicer.manticore_instances.values(): 30 | mwrapper.manticore_object.kill() 31 | stime = time.time() 32 | while mwrapper.thread.is_alive(): 33 | time.sleep(1) 34 | if (time.time() - stime) > 15: 35 | break 36 | 37 | @classmethod 38 | def tearDownClass(cls): 39 | for f in glob.glob("mcore_*"): 40 | if Path(f).is_dir(): 41 | rmtree(f, ignore_errors=True) 42 | 43 | def test_start_with_no_or_invalid_contract_path(self): 44 | self.servicer.StartEVM(EVMArguments(solc_bin=self.solc_path), self.context) 45 | self.assertEqual(self.context.code, grpc.StatusCode.INVALID_ARGUMENT) 46 | self.assertEqual(self.context.details, "Contract path not specified!") 47 | 48 | self.context.reset() 49 | 50 | invalid_contract_path = str( 51 | self.dirname / Path("contracts") / Path("invalid_contract") 52 | ) 53 | self.servicer.StartEVM( 54 | EVMArguments(contract_path=invalid_contract_path, solc_bin=self.solc_path), 55 | self.context, 56 | ) 57 | 58 | self.assertEqual(self.context.code, grpc.StatusCode.INVALID_ARGUMENT) 59 | self.assertEqual( 60 | self.context.details, f"Contract path invalid: '{invalid_contract_path}'" 61 | ) 62 | 63 | def test_start_with_no_solc_specified_or_in_path(self): 64 | path_to_use = os.environ["PATH"] 65 | solc_on_path = which("solc") 66 | 67 | if solc_on_path: 68 | solc_dir = str(Path(solc_on_path).parent) 69 | cur_paths = os.environ["PATH"].split(os.pathsep) 70 | 71 | path_to_use = str(os.pathsep).join( 72 | [dir for dir in cur_paths if dir != solc_dir] 73 | ) 74 | 75 | with unittest.mock.patch.dict(os.environ, {"PATH": path_to_use}): 76 | mcore_instance = self.servicer.StartEVM( 77 | EVMArguments(contract_path=self.contract_path), 78 | self.context, 79 | ) 80 | 81 | self.assertEqual(self.context.code, grpc.StatusCode.INVALID_ARGUMENT) 82 | self.assertEqual( 83 | self.context.details, 84 | "solc binary neither specified in EVMArguments nor found in PATH!", 85 | ) 86 | 87 | def test_start(self): 88 | mcore_instance = self.servicer.StartEVM( 89 | EVMArguments(contract_path=self.contract_path, solc_bin=self.solc_path), 90 | self.context, 91 | ) 92 | 93 | try: 94 | UUID(mcore_instance.uuid) 95 | except ValueError: 96 | self.fail( 97 | "Start() returned ManticoreInstance with missing or malformed UUID" 98 | ) 99 | 100 | self.assertTrue(mcore_instance.uuid in self.servicer.manticore_instances) 101 | 102 | mcore = self.servicer.manticore_instances[mcore_instance.uuid].manticore_object 103 | self.assertTrue(Path(mcore.workspace).is_dir()) 104 | 105 | def test_terminate_running_manticore(self): 106 | mcore_instance = self.servicer.StartEVM( 107 | EVMArguments(contract_path=self.contract_path, solc_bin=self.solc_path), 108 | self.context, 109 | ) 110 | mwrapper = self.servicer.manticore_instances[mcore_instance.uuid] 111 | 112 | stime = time.time() 113 | while not mwrapper.manticore_object.is_running(): 114 | if (time.time() - stime) > 5: 115 | self.fail( 116 | f"Manticore instance {mcore_instance.uuid} failed to start running before timeout" 117 | ) 118 | time.sleep(1) 119 | 120 | self.context.reset() 121 | 122 | self.servicer.Terminate(mcore_instance, self.context) 123 | self.assertEqual(self.context.code, grpc.StatusCode.OK) 124 | self.assertTrue(mwrapper.manticore_object.is_killed()) 125 | 126 | stime = time.time() 127 | while mwrapper.manticore_object.is_running(): 128 | if (time.time() - stime) > 10: 129 | self.fail( 130 | f"Manticore instance {mcore_instance.uuid} failed to stop running before timeout" 131 | ) 132 | time.sleep(1) 133 | 134 | def test_terminate_killed_manticore(self): 135 | mcore_instance = self.servicer.StartEVM( 136 | EVMArguments(contract_path=self.contract_path, solc_bin=self.solc_path), 137 | self.context, 138 | ) 139 | 140 | self.context.reset() 141 | 142 | mwrapper = self.servicer.manticore_instances[mcore_instance.uuid] 143 | mwrapper.manticore_object.kill() 144 | stime = time.time() 145 | while mwrapper.manticore_object.is_running(): 146 | if (time.time() - stime) > 10: 147 | self.fail( 148 | f"Manticore instance {mcore_instance.uuid} failed to stop running before timeout" 149 | ) 150 | time.sleep(1) 151 | 152 | self.servicer.Terminate(mcore_instance, self.context) 153 | self.assertEqual(self.context.code, grpc.StatusCode.OK) 154 | 155 | def test_terminate_invalid_manticore(self): 156 | t_status = self.servicer.Terminate( 157 | ManticoreInstance(uuid=uuid4().hex), self.context 158 | ) 159 | self.assertEqual(self.context.code, grpc.StatusCode.FAILED_PRECONDITION) 160 | 161 | def test_get_message_list_running_manticore(self): 162 | mcore_instance = self.servicer.StartEVM( 163 | EVMArguments(contract_path=self.contract_path, solc_bin=self.solc_path), 164 | self.context, 165 | ) 166 | mwrapper = self.servicer.manticore_instances[mcore_instance.uuid] 167 | 168 | stime = time.time() 169 | while mwrapper.manticore_object._log_queue.empty() and time.time() - stime < 5: 170 | time.sleep(1) 171 | if not mwrapper.manticore_object._log_queue.empty(): 172 | deque_messages = list(mwrapper.manticore_object._log_queue) 173 | messages = self.servicer.GetMessageList( 174 | mcore_instance, self.context 175 | ).messages 176 | for i in range(len(messages)): 177 | self.assertEqual(messages[i].content, deque_messages[i]) 178 | break 179 | 180 | def test_get_message_list_stopped_manticore(self): 181 | mcore_instance = self.servicer.StartEVM( 182 | EVMArguments(contract_path=self.contract_path, solc_bin=self.solc_path), 183 | self.context, 184 | ) 185 | mwrapper = self.servicer.manticore_instances[mcore_instance.uuid] 186 | 187 | mwrapper.manticore_object.kill() 188 | stime = time.time() 189 | while mwrapper.manticore_object.is_running(): 190 | if (time.time() - stime) > 10: 191 | self.fail( 192 | f"Manticore instance {mcore_instance.uuid} failed to stop running before timeout" 193 | ) 194 | time.sleep(1) 195 | 196 | stime = time.time() 197 | while mwrapper.manticore_object._log_queue.empty() and time.time() - stime < 5: 198 | time.sleep(1) 199 | if not mwrapper.manticore_object._log_queue.empty(): 200 | deque_messages = list(mwrapper.manticore_object._log_queue) 201 | messages = self.servicer.GetMessageList( 202 | mcore_instance, self.context 203 | ).messages 204 | for i in range(len(messages)): 205 | self.assertEqual(messages[i].content, deque_messages[i]) 206 | break 207 | 208 | def test_get_message_list_invalid_manticore(self): 209 | message_list = self.servicer.GetMessageList( 210 | ManticoreInstance(uuid=uuid4().hex), self.context 211 | ) 212 | self.assertEqual(self.context.code, grpc.StatusCode.FAILED_PRECONDITION) 213 | self.assertEqual( 214 | self.context.details, "Specified Manticore instance not found!" 215 | ) 216 | self.assertEqual(len(message_list.messages), 0) 217 | 218 | def test_get_state_list_running_manticore(self): 219 | mcore_instance = self.servicer.StartEVM( 220 | EVMArguments(contract_path=self.contract_path, solc_bin=self.solc_path), 221 | self.context, 222 | ) 223 | mwrapper = self.servicer.manticore_instances[mcore_instance.uuid] 224 | 225 | for i in range(5): 226 | time.sleep(1) 227 | state_list = self.servicer.GetStateList(mcore_instance, self.context) 228 | all_states = list( 229 | map( 230 | lambda x: x.state_id, 231 | list(state_list.active_states) 232 | + list(state_list.waiting_states) 233 | + list(state_list.forked_states) 234 | + list(state_list.errored_states) 235 | + list(state_list.complete_states), 236 | ) 237 | ) 238 | state_ids = mwrapper.manticore_object.introspect().keys() 239 | 240 | for sid in state_ids: 241 | self.assertIn(sid, all_states) 242 | 243 | def test_get_state_list_stopped_manticore(self): 244 | mcore_instance = self.servicer.StartEVM( 245 | EVMArguments(contract_path=self.contract_path, solc_bin=self.solc_path), 246 | self.context, 247 | ) 248 | mwrapper = self.servicer.manticore_instances[mcore_instance.uuid] 249 | 250 | mwrapper.manticore_object.kill() 251 | stime = time.time() 252 | while mwrapper.manticore_object.is_running(): 253 | if (time.time() - stime) > 10: 254 | self.fail( 255 | f"Manticore instance {mcore_instance.uuid} failed to stop running before timeout" 256 | ) 257 | time.sleep(1) 258 | 259 | stime = time.time() 260 | for i in range(5): 261 | time.sleep(1) 262 | state_list = self.servicer.GetStateList(mcore_instance, self.context) 263 | all_states = list( 264 | map( 265 | lambda x: x.state_id, 266 | list(state_list.active_states) 267 | + list(state_list.waiting_states) 268 | + list(state_list.forked_states) 269 | + list(state_list.errored_states) 270 | + list(state_list.complete_states), 271 | ) 272 | ) 273 | state_ids = mwrapper.manticore_object.introspect().keys() 274 | 275 | for sid in state_ids: 276 | self.assertIn(sid, all_states) 277 | 278 | def test_get_state_list_invalid_manticore(self): 279 | state_list = self.servicer.GetStateList( 280 | ManticoreInstance(uuid=uuid4().hex), self.context 281 | ) 282 | 283 | self.assertFalse(state_list.active_states) 284 | self.assertFalse(state_list.waiting_states) 285 | self.assertFalse(state_list.forked_states) 286 | self.assertFalse(state_list.errored_states) 287 | self.assertFalse(state_list.complete_states) 288 | 289 | def test_check_manticore_running(self): 290 | mcore_instance = self.servicer.StartEVM( 291 | EVMArguments(contract_path=self.contract_path, solc_bin=self.solc_path), 292 | self.context, 293 | ) 294 | mwrapper = self.servicer.manticore_instances[mcore_instance.uuid] 295 | 296 | stime = time.time() 297 | while not mwrapper.manticore_object.is_running(): 298 | if (time.time() - stime) > 10: 299 | self.fail( 300 | f"Manticore instance {mcore_instance.uuid} failed to start running before timeout" 301 | ) 302 | time.sleep(1) 303 | 304 | self.assertTrue( 305 | self.servicer.CheckManticoreRunning(mcore_instance, self.context).is_running 306 | ) 307 | 308 | mwrapper.manticore_object.kill() 309 | 310 | stime = time.time() 311 | while mwrapper.thread.is_alive(): 312 | if (time.time() - stime) > 45: 313 | self.fail( 314 | f"Manticore instance {mcore_instance.uuid} failed to stop running and finish generating reports before timeout" 315 | ) 316 | time.sleep(1) 317 | 318 | self.assertFalse( 319 | self.servicer.CheckManticoreRunning(mcore_instance, self.context).is_running 320 | ) 321 | 322 | def test_check_manticore_running_invalid_manticore(self): 323 | self.assertFalse( 324 | self.servicer.CheckManticoreRunning( 325 | ManticoreInstance(uuid=uuid4().hex), self.context 326 | ).is_running 327 | ) 328 | 329 | def test_stop_server(self): 330 | self.servicer.StartEVM( 331 | EVMArguments(contract_path=self.contract_path, solc_bin=self.solc_path), 332 | self.context, 333 | ) 334 | self.servicer.StartEVM( 335 | EVMArguments(contract_path=self.contract_path, solc_bin=self.solc_path), 336 | self.context, 337 | ) 338 | 339 | self.servicer.StopServer(StopServerRequest(), self.context) 340 | 341 | self.assertTrue(self.test_event.is_set()) 342 | for mwrapper in self.servicer.manticore_instances.values(): 343 | self.assertFalse(mwrapper.manticore_object.is_running()) 344 | -------------------------------------------------------------------------------- /MUICore/tests/test_native.py: -------------------------------------------------------------------------------- 1 | import glob 2 | import threading 3 | import time 4 | import unittest 5 | from inspect import currentframe, getframeinfo 6 | from pathlib import Path 7 | from shutil import rmtree 8 | from uuid import UUID, uuid4 9 | 10 | import grpc 11 | 12 | from muicore import mui_server 13 | from muicore.MUICore_pb2 import * 14 | from tests.mock_classes import MockContext 15 | 16 | 17 | class MUICoreNativeTest(unittest.TestCase): 18 | def setUp(self): 19 | self.dirname = str(Path(getframeinfo(currentframe()).filename).resolve().parent) 20 | self.binary_path = str( 21 | self.dirname / Path("binaries") / Path("arguments_linux_amd64") 22 | ) 23 | self.test_event = threading.Event() 24 | self.servicer = mui_server.MUIServicer(self.test_event) 25 | self.context = MockContext() 26 | 27 | def tearDown(self): 28 | for mwrapper in self.servicer.manticore_instances.values(): 29 | mwrapper.manticore_object.kill() 30 | stime = time.time() 31 | while mwrapper.thread.is_alive(): 32 | time.sleep(1) 33 | if (time.time() - stime) > 15: 34 | break 35 | 36 | @classmethod 37 | def tearDownClass(cls): 38 | for f in glob.glob("mcore_*"): 39 | if Path(f).is_dir(): 40 | rmtree(f, ignore_errors=True) 41 | 42 | def test_start_with_no_or_invalid_binary_path(self): 43 | self.servicer.StartNative(NativeArguments(), self.context) 44 | 45 | self.assertEquals(self.context.code, grpc.StatusCode.INVALID_ARGUMENT) 46 | self.assertEquals(self.context.details, "Basic arguments are invalid!") 47 | 48 | self.context.reset() 49 | 50 | invalid_binary_path = str( 51 | self.dirname / Path("binaries") / Path("invalid_binary") 52 | ) 53 | 54 | self.servicer.StartNative( 55 | NativeArguments(program_path=invalid_binary_path), self.context 56 | ) 57 | 58 | self.assertEquals(self.context.code, grpc.StatusCode.INVALID_ARGUMENT) 59 | self.assertEquals(self.context.details, "Basic arguments are invalid!") 60 | 61 | def test_start(self): 62 | mcore_instance = self.servicer.StartNative( 63 | NativeArguments(program_path=self.binary_path), self.context 64 | ) 65 | 66 | try: 67 | UUID(mcore_instance.uuid) 68 | except ValueError: 69 | self.fail( 70 | "Start() returned ManticoreInstance with missing or malformed UUID" 71 | ) 72 | 73 | self.assertTrue(mcore_instance.uuid in self.servicer.manticore_instances) 74 | 75 | mcore = self.servicer.manticore_instances[mcore_instance.uuid].manticore_object 76 | self.assertTrue(Path(mcore.workspace).is_dir()) 77 | 78 | def test_start_with_find_avoid_hooks(self): 79 | mcore_instance = self.servicer.StartNative( 80 | NativeArguments( 81 | program_path=str(self.binary_path), 82 | hooks=[ 83 | Hook(type=Hook.HookType.FIND, address=0x40100C), 84 | Hook(type=Hook.HookType.AVOID, address=0x401018), 85 | ], 86 | ), 87 | self.context, 88 | ) 89 | self.assertTrue(mcore_instance.uuid in self.servicer.manticore_instances) 90 | # TODO: Once logging is improved, check that find_f outputs successful stdin. Might require different test binary. 91 | 92 | def test_start_with_global_hook(self): 93 | mcore_instance = self.servicer.StartNative( 94 | NativeArguments( 95 | program_path=str(self.binary_path), 96 | hooks=[ 97 | Hook( 98 | type=Hook.HookType.GLOBAL, 99 | func_text="\n".join( 100 | ( 101 | "global m", 102 | "def hook(state):", 103 | " m.test_attribute = True", 104 | " m.kill()", 105 | "m.hook(None)(hook)", 106 | ) 107 | ), 108 | ), 109 | ], 110 | ), 111 | self.context, 112 | ) 113 | 114 | self.assertTrue(mcore_instance.uuid in self.servicer.manticore_instances) 115 | m = self.servicer.manticore_instances[mcore_instance.uuid].manticore_object 116 | 117 | stime = time.time() 118 | while not hasattr(m, "test_attribute"): 119 | if (time.time() - stime) > 10: 120 | self.fail( 121 | f"Global hook failed to trigger on {mcore_instance.uuid} before timeout" 122 | ) 123 | time.sleep(1) 124 | 125 | self.assertTrue(m.test_attribute) 126 | 127 | def test_start_with_custom_hook(self): 128 | mcore_instance = self.servicer.StartNative( 129 | NativeArguments( 130 | program_path=str(self.binary_path), 131 | hooks=[ 132 | Hook( 133 | type=Hook.HookType.CUSTOM, 134 | func_text="\n".join( 135 | ( 136 | "global m, addr", 137 | "def hook(state):", 138 | " m.test_attribute = addr", 139 | " m.kill()", 140 | "m.hook(addr)(hook)", 141 | ) 142 | ), 143 | address=0x400FDC, 144 | ), 145 | ], 146 | ), 147 | self.context, 148 | ) 149 | 150 | self.assertTrue(mcore_instance.uuid in self.servicer.manticore_instances) 151 | m = self.servicer.manticore_instances[mcore_instance.uuid].manticore_object 152 | 153 | stime = time.time() 154 | while not hasattr(m, "test_attribute"): 155 | if (time.time() - stime) > 10: 156 | self.fail( 157 | f"Custom hook failed to trigger on {mcore_instance.uuid} before timeout" 158 | ) 159 | time.sleep(1) 160 | 161 | self.assertTrue(m.test_attribute == 0x400FDC) 162 | 163 | def test_start_with_invalid_custom_and_global_hook(self): 164 | 165 | self.servicer.StartNative( 166 | NativeArguments( 167 | program_path=str(self.binary_path), 168 | hooks=[ 169 | Hook( 170 | type=Hook.HookType.CUSTOM, 171 | func_text="this is an invalid hook", 172 | address=0x400FDC, 173 | ), 174 | ], 175 | ), 176 | self.context, 177 | ) 178 | 179 | self.assertEquals(self.context.code, grpc.StatusCode.INVALID_ARGUMENT) 180 | self.assertEquals(self.context.details, "Hooks set are invalid!") 181 | 182 | self.context.reset() 183 | 184 | self.servicer.StartNative( 185 | NativeArguments( 186 | program_path=str(self.binary_path), 187 | hooks=[ 188 | Hook( 189 | type=Hook.HookType.GLOBAL, 190 | func_text="this is another invalid hook", 191 | ), 192 | ], 193 | ), 194 | self.context, 195 | ) 196 | 197 | self.assertEquals(self.context.code, grpc.StatusCode.INVALID_ARGUMENT) 198 | self.assertEquals(self.context.details, "Hooks set are invalid!") 199 | 200 | def test_terminate_running_manticore(self): 201 | mcore_instance = self.servicer.StartNative( 202 | NativeArguments(program_path=self.binary_path), self.context 203 | ) 204 | mwrapper = self.servicer.manticore_instances[mcore_instance.uuid] 205 | 206 | stime = time.time() 207 | while not mwrapper.manticore_object.is_running(): 208 | if (time.time() - stime) > 5: 209 | self.fail( 210 | f"Manticore instance {mcore_instance.uuid} failed to start running before timeout" 211 | ) 212 | time.sleep(1) 213 | 214 | self.context.reset() 215 | 216 | self.servicer.Terminate(mcore_instance, self.context) 217 | self.assertEqual(self.context.code, grpc.StatusCode.OK) 218 | self.assertTrue(mwrapper.manticore_object.is_killed()) 219 | 220 | stime = time.time() 221 | while mwrapper.manticore_object.is_running(): 222 | if (time.time() - stime) > 10: 223 | self.fail( 224 | f"Manticore instance {mcore_instance.uuid} failed to stop running before timeout" 225 | ) 226 | time.sleep(1) 227 | 228 | def test_terminate_killed_manticore(self): 229 | mcore_instance = self.servicer.StartNative( 230 | NativeArguments(program_path=self.binary_path), self.context 231 | ) 232 | mwrapper = self.servicer.manticore_instances[mcore_instance.uuid] 233 | mwrapper.manticore_object.kill() 234 | stime = time.time() 235 | while mwrapper.manticore_object.is_running(): 236 | if (time.time() - stime) > 10: 237 | self.fail( 238 | f"Manticore instance {mcore_instance.uuid} failed to stop running before timeout" 239 | ) 240 | time.sleep(1) 241 | 242 | t_status = self.servicer.Terminate(mcore_instance, self.context) 243 | 244 | self.assertEquals(self.context.code, grpc.StatusCode.OK) 245 | 246 | def test_terminate_invalid_manticore(self): 247 | t_status = self.servicer.Terminate( 248 | ManticoreInstance(uuid=uuid4().hex), self.context 249 | ) 250 | self.assertEqual(self.context.code, grpc.StatusCode.FAILED_PRECONDITION) 251 | 252 | def test_get_message_list_running_manticore(self): 253 | mcore_instance = self.servicer.StartNative( 254 | NativeArguments(program_path=self.binary_path), self.context 255 | ) 256 | mwrapper = self.servicer.manticore_instances[mcore_instance.uuid] 257 | 258 | stime = time.time() 259 | while mwrapper.manticore_object._log_queue.empty() and time.time() - stime < 5: 260 | time.sleep(1) 261 | if not mwrapper.manticore_object._log_queue.empty(): 262 | deque_messages = list(mwrapper.manticore_object._log_queue) 263 | messages = self.servicer.GetMessageList( 264 | mcore_instance, self.context 265 | ).messages 266 | for i in range(len(messages)): 267 | self.assertEqual(messages[i].content, deque_messages[i]) 268 | break 269 | 270 | def test_get_message_list_stopped_manticore(self): 271 | mcore_instance = self.servicer.StartNative( 272 | NativeArguments(program_path=self.binary_path), self.context 273 | ) 274 | mwrapper = self.servicer.manticore_instances[mcore_instance.uuid] 275 | 276 | mwrapper.manticore_object.kill() 277 | stime = time.time() 278 | while mwrapper.manticore_object.is_running(): 279 | if (time.time() - stime) > 10: 280 | self.fail( 281 | f"Manticore instance {mcore_instance.uuid} failed to stop running before timeout" 282 | ) 283 | time.sleep(1) 284 | 285 | stime = time.time() 286 | while mwrapper.manticore_object._log_queue.empty() and time.time() - stime < 5: 287 | time.sleep(1) 288 | if not mwrapper.manticore_object._log_queue.empty(): 289 | deque_messages = list(mwrapper.manticore_object._log_queue) 290 | messages = self.servicer.GetMessageList( 291 | mcore_instance, self.context 292 | ).messages 293 | for i in range(len(messages)): 294 | self.assertEqual(messages[i].content, deque_messages[i]) 295 | break 296 | 297 | def test_get_message_list_invalid_manticore(self): 298 | message_list = self.servicer.GetMessageList( 299 | ManticoreInstance(uuid=uuid4().hex), self.context 300 | ) 301 | self.assertEqual(self.context.code, grpc.StatusCode.FAILED_PRECONDITION) 302 | self.assertEqual( 303 | self.context.details, "Specified Manticore instance not found!" 304 | ) 305 | self.assertEqual(len(message_list.messages), 0) 306 | 307 | def test_get_state_list_running_manticore(self): 308 | mcore_instance = self.servicer.StartNative( 309 | NativeArguments(program_path=self.binary_path), self.context 310 | ) 311 | mwrapper = self.servicer.manticore_instances[mcore_instance.uuid] 312 | 313 | for i in range(5): 314 | time.sleep(1) 315 | state_list = self.servicer.GetStateList(mcore_instance, self.context) 316 | all_states = list( 317 | map( 318 | lambda x: x.state_id, 319 | list(state_list.active_states) 320 | + list(state_list.waiting_states) 321 | + list(state_list.forked_states) 322 | + list(state_list.errored_states) 323 | + list(state_list.complete_states), 324 | ) 325 | ) 326 | state_ids = mwrapper.manticore_object.introspect().keys() 327 | 328 | for sid in state_ids: 329 | self.assertIn(sid, all_states) 330 | 331 | def test_get_state_list_stopped_manticore(self): 332 | mcore_instance = self.servicer.StartNative( 333 | NativeArguments(program_path=self.binary_path), self.context 334 | ) 335 | mwrapper = self.servicer.manticore_instances[mcore_instance.uuid] 336 | 337 | mwrapper.manticore_object.kill() 338 | stime = time.time() 339 | while mwrapper.manticore_object.is_running(): 340 | if (time.time() - stime) > 10: 341 | self.fail( 342 | f"Manticore instance {mcore_instance.uuid} failed to stop running before timeout" 343 | ) 344 | time.sleep(1) 345 | 346 | stime = time.time() 347 | for i in range(5): 348 | time.sleep(1) 349 | state_list = self.servicer.GetStateList(mcore_instance, self.context) 350 | all_states = list( 351 | map( 352 | lambda x: x.state_id, 353 | list(state_list.active_states) 354 | + list(state_list.waiting_states) 355 | + list(state_list.forked_states) 356 | + list(state_list.errored_states) 357 | + list(state_list.complete_states), 358 | ) 359 | ) 360 | state_ids = mwrapper.manticore_object.introspect().keys() 361 | 362 | for sid in state_ids: 363 | self.assertIn(sid, all_states) 364 | 365 | def test_get_state_list_invalid_manticore(self): 366 | state_list = self.servicer.GetStateList( 367 | ManticoreInstance(uuid=uuid4().hex), self.context 368 | ) 369 | 370 | self.assertFalse(state_list.active_states) 371 | self.assertFalse(state_list.waiting_states) 372 | self.assertFalse(state_list.forked_states) 373 | self.assertFalse(state_list.errored_states) 374 | self.assertFalse(state_list.complete_states) 375 | 376 | def test_check_manticore_running(self): 377 | mcore_instance = self.servicer.StartNative( 378 | NativeArguments(program_path=self.binary_path), self.context 379 | ) 380 | mwrapper = self.servicer.manticore_instances[mcore_instance.uuid] 381 | 382 | stime = time.time() 383 | while not mwrapper.manticore_object.is_running(): 384 | if (time.time() - stime) > 10: 385 | self.fail( 386 | f"Manticore instance {mcore_instance.uuid} failed to start running before timeout" 387 | ) 388 | time.sleep(1) 389 | 390 | self.assertTrue( 391 | self.servicer.CheckManticoreRunning(mcore_instance, self.context).is_running 392 | ) 393 | 394 | mwrapper.manticore_object.kill() 395 | 396 | stime = time.time() 397 | while mwrapper.thread.is_alive(): 398 | if (time.time() - stime) > 45: 399 | self.fail( 400 | f"Manticore instance {mcore_instance.uuid} failed to stop running and finish generating reports before timeout" 401 | ) 402 | time.sleep(1) 403 | 404 | self.assertFalse( 405 | self.servicer.CheckManticoreRunning(mcore_instance, self.context).is_running 406 | ) 407 | 408 | def test_check_manticore_running_invalid_manticore(self): 409 | self.assertFalse( 410 | self.servicer.CheckManticoreRunning( 411 | ManticoreInstance(uuid=uuid4().hex), self.context 412 | ).is_running 413 | ) 414 | 415 | def test_stop_server(self): 416 | self.servicer.StartNative( 417 | NativeArguments(program_path=self.binary_path), self.context 418 | ) 419 | self.servicer.StartNative( 420 | NativeArguments(program_path=self.binary_path), self.context 421 | ) 422 | 423 | self.servicer.StopServer(StopServerRequest(), self.context) 424 | 425 | self.assertTrue(self.test_event.is_set()) 426 | for mwrapper in self.servicer.manticore_instances.values(): 427 | self.assertFalse(mwrapper.manticore_object.is_running()) 428 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # MUI-Ghidra 2 | MUI support for Ghidra. This is primarily a prototype repository. See the main [MUI repo](https://github.com/trailofbits/mui) for a more complete implementation. 3 | 4 | This repository is also the temporary home for [MUI-Core](MUICore/README.md). 5 | 6 | # Usage 7 | 8 | At its present form, MUI-Ghidra manifests as three Ghidra components named `MUI Setup` (used to specify args and run Manticore), `MUI Log`, and `MUI State List` (which together display Manticore output). 9 | 10 | 1. To run Manticore on the current binary, open the `MUI Setup` component via `MUI -> Run Manticore` in the menu. 11 | 2. Fill in Manticore and program arguments in the `MUI Setup` component, and click the `Run` Button. Notably, users can specify: 12 | - the Manticore binary used (by default, a bundled binary which requires `python3.9` on PATH is used) 13 | - the port used by Manticore's state server (by default, an open port starting from `3215` will be allocated). 14 | 3. View log message output and a list of states and their statuses via the `MUI Log`/`MUI State List` components which will be visible on `Run`. Alternatively, you can open the components manually via `MUI -> Show Log / Show State List` in the menu. 15 | 16 | ## Components 17 | 18 | ### Setup 19 | - The `MUI Setup` component allows you to specify key `manticore` arguments 20 | - You may add additional arguments in the `Extra Manticore Arguments` field at the bottom of the panel 21 | - Click `Run` to being an instance of Manticore with your desired arguments 22 | - You may run multiple Manticore instances at once 23 | 24 |

25 | Image 26 |

27 | 28 | ### Log 29 | - At present, `stdout` from `manticore` is output to the log 30 | - You may stop the execution of manticore and clear the log with the Stop and Clear buttons on the toolbar 31 | - You can switch between Manticore instances by clicking on their respective log tabs 32 | - Closing a log tab will stop the execution of the Manticore instance associated with it 33 | 34 |

35 | Image 36 |

37 | 38 | ### State List 39 | - The State List displays the states and their statuses of the Manticore instance whose log tab is currently being viewed 40 | - Switching log tabs will cause the State List to show the state list of the newly-focused Manticore instance 41 | - You may click on the State statuses to expand a list of States with that status alongside their respective IDs 42 | - At present, possible State statuses include `ACTIVE`, `WAITING`, `FORKED`, `COMPLETE`, and `ERRORED` 43 | 44 |

45 | Image 46 |

47 | 48 | ### Find/Avoid Address 49 | - Right-clicking on an address/instruction in the Listing component (which displays the analyzed program's disassembly) will reveal two new Menu options: `MUI -> Toggle Find Instruction` and `MUI -> Toggle Avoid Instruction` 50 | - Setting an address/instruction to `Find` will highlight it Green, and setting it to `Avoid` will highlight it Red 51 | - However, this feature is currently still **IN DEVELOPMENT** and setting addresses to `Find`/`Avoid` will have no effect 52 | - A warning in the MUI Setup component should remind users that the feature is still unimplemented if any addresses are set to `Find`/`Avoid` 53 | 54 |

55 | Image 56 |

57 | 58 | 59 | # Building 60 | 61 | Build the plugin with Gradle. Built plugin will be a `zip` file in `dist` directory. 62 | 63 | ```bash 64 | cd MUI/ 65 | GHIDRA_INSTALL_DIR= gradle 66 | ``` 67 | 68 | # Installation 69 | 70 | 1. Ensure that Python 3.9 is installed (and that you have a `python3.9` binary). Manticore is bundled with the plugin and does not need to be separately installed, but currently requires python3.9. 71 | 72 | * Note: You can build this for yourself by using the [`shiv`](https://shiv.readthedocs.io/en/latest/) tool and running the following: 73 | ```sh 74 | shiv --reproducible -c manticore -o ./os/linux_x86_64/manticore /manticore[native] 75 | ``` 76 | 2. Copy the zip file to the `Extensions` folder in your Ghidra directory 77 | 3. Run Ghidra and select the extension in `File -> Install Extensions` 78 | 4. Restart Ghidra 79 | 80 | # Development 81 | 82 | 1. Fork and clone the repo 83 | 2. Install the [GhidraDev plugin](https://github.com/NationalSecurityAgency/ghidra/blob/master/GhidraBuild/EclipsePlugins/GhidraDev/GhidraDevPlugin/GhidraDev_README.html) in Eclipse 84 | 3. Import the project via `File -> Import -> General -> Projects from Folder or Archive` 85 | 4. Link your installation of Ghidra via `GhidraDev -> Link Ghidra`. The necessary `.project` and `.pydevproject` files will be generated for Eclipse. 86 | 5. Format your code with the included `MUI/GhidraEclipseFormatter.xml` (taken from upstream Ghidra) by running `just format` with the tool [just](https://github.com/casey/just). 87 | 6. When you first build the plugin, a protobuf compiler binary will generate the `StateOuterClass.java` file used for Manticore message & state list deserialization. 88 | -------------------------------------------------------------------------------- /justfile: -------------------------------------------------------------------------------- 1 | format: 2 | just --justfile MUICore/justfile format 3 | gradle -p MUI spotlessApply 4 | 5 | lint: 6 | just --justfile MUICore/justfile lint 7 | gradle -p MUI spotlessCheck 8 | 9 | install: 10 | just --justfile MUICore/justfile install 11 | gradle -p MUI install 12 | --------------------------------------------------------------------------------