mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2024-11-21 19:42:14 +00:00
Added extensions GnuDisassembler and SleighDevTools
This commit is contained in:
parent
cdbe651627
commit
a15c70950f
@ -1,3 +1,5 @@
|
|||||||
|
apply from: file("../gpl.gradle").getCanonicalPath()
|
||||||
|
|
||||||
if (findProject(':Generic') != null) {
|
if (findProject(':Generic') != null) {
|
||||||
apply from: "$rootProject.projectDir/gradle/nativeProject.gradle"
|
apply from: "$rootProject.projectDir/gradle/nativeProject.gradle"
|
||||||
apply from: "$rootProject.projectDir/gradle/distributableGPLModule.gradle"
|
apply from: "$rootProject.projectDir/gradle/distributableGPLModule.gradle"
|
||||||
|
@ -4,3 +4,4 @@
|
|||||||
Module.manifest||Public Domain||||END|
|
Module.manifest||Public Domain||||END|
|
||||||
build.gradle||Public Domain||||END|
|
build.gradle||Public Domain||||END|
|
||||||
data/cabextract-1.6.tar.gz||GPL 3||||END|
|
data/cabextract-1.6.tar.gz||GPL 3||||END|
|
||||||
|
settings.gradle||Public Domain||||END|
|
||||||
|
0
GPL/CabExtract/settings.gradle
Normal file
0
GPL/CabExtract/settings.gradle
Normal file
@ -1,13 +1,13 @@
|
|||||||
|
apply from: file("../gpl.gradle").getCanonicalPath()
|
||||||
|
|
||||||
if (findProject(':Generic') != null) {
|
if (findProject(':Generic') != null) {
|
||||||
apply from: "$rootProject.projectDir/gradle/javaProject.gradle"
|
apply from: "$rootProject.projectDir/gradle/javaProject.gradle"
|
||||||
apply from: "$rootProject.projectDir/gradle/distributableGPLModule.gradle"
|
apply from: "$rootProject.projectDir/gradle/distributableGPLModule.gradle"
|
||||||
|
|
||||||
rootProject.assembleDistribution {
|
rootProject.assembleDistribution {
|
||||||
|
|
||||||
doLast {
|
doLast {
|
||||||
// eliminate standard module lib directory
|
// eliminate standard module lib directory
|
||||||
def assemblePath = destinationDir.path + "/" + getZipPath(this.project)
|
def assemblePath = destinationDir.path + "/" + getZipPath(this.project)
|
||||||
println "DELETE: ${assemblePath}/lib"
|
|
||||||
delete assemblePath + "/lib"
|
delete assemblePath + "/lib"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -28,6 +28,7 @@ eclipse.project.name = 'GPL DMG'
|
|||||||
*
|
*
|
||||||
*********************************************************************************/
|
*********************************************************************************/
|
||||||
sourceSets {
|
sourceSets {
|
||||||
|
|
||||||
dmg {
|
dmg {
|
||||||
java {
|
java {
|
||||||
srcDir 'src/dmg/java'
|
srcDir 'src/dmg/java'
|
||||||
|
@ -16,3 +16,4 @@ data/os/win64/llio_amd64.dll||GPL 3||||END|
|
|||||||
data/os/win64/llio_i386.dll||GPL 3||||END|
|
data/os/win64/llio_i386.dll||GPL 3||||END|
|
||||||
data/os/win64/llio_ia64.dll||GPL 3||||END|
|
data/os/win64/llio_ia64.dll||GPL 3||||END|
|
||||||
data/server_memory.cfg||Public Domain||||END|
|
data/server_memory.cfg||Public Domain||||END|
|
||||||
|
settings.gradle||Public Domain||||END|
|
||||||
|
0
GPL/DMG/settings.gradle
Normal file
0
GPL/DMG/settings.gradle
Normal file
@ -1,3 +1,4 @@
|
|||||||
|
apply from: file("../gpl.gradle").getCanonicalPath()
|
||||||
|
|
||||||
if (findProject(':Generic') != null) {
|
if (findProject(':Generic') != null) {
|
||||||
apply from: "$rootProject.projectDir/gradle/nativeProject.gradle"
|
apply from: "$rootProject.projectDir/gradle/nativeProject.gradle"
|
||||||
|
@ -6,4 +6,5 @@
|
|||||||
##MODULE IP: Public Domain
|
##MODULE IP: Public Domain
|
||||||
Module.manifest||Public Domain||||END|
|
Module.manifest||Public Domain||||END|
|
||||||
build.gradle||Public Domain||||END|
|
build.gradle||Public Domain||||END|
|
||||||
|
settings.gradle||Public Domain||||END|
|
||||||
src/demangler_gnu/README.txt||Public Domain||||END|
|
src/demangler_gnu/README.txt||Public Domain||||END|
|
||||||
|
0
GPL/DemanglerGnu/settings.gradle
Normal file
0
GPL/DemanglerGnu/settings.gradle
Normal file
0
GPL/GnuDisassembler/Module.manifest
Normal file
0
GPL/GnuDisassembler/Module.manifest
Normal file
20
GPL/GnuDisassembler/README.txt
Normal file
20
GPL/GnuDisassembler/README.txt
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
The GnuDisassembler extension module must be built using gradle prior to its' use within Ghidra.
|
||||||
|
|
||||||
|
This module provides the ability to leverage the binutils disassembler capabilities
|
||||||
|
for various processors as a means of verifying Sleigh disassembler output syntax.
|
||||||
|
|
||||||
|
To build this extension for Linux or Mac OS X:
|
||||||
|
|
||||||
|
1. If building for an installation of Ghidra, copy the appropriate source distribution of
|
||||||
|
binutils into this module's root directory. If building within a git clone of the full
|
||||||
|
Ghidra source, copy binutils source distribution file into the ghidra.bin/GPL/GnuDisassembler
|
||||||
|
directory.
|
||||||
|
|
||||||
|
The supported version and archive format is identified within the build.gradle file.
|
||||||
|
If a different binutils distribution is used the build.gradle and/or buildGdis.gradle
|
||||||
|
may require modification.
|
||||||
|
|
||||||
|
2. Run gradle from the module's root directory (see top of build.gradle file for
|
||||||
|
specific instructions).
|
||||||
|
|
||||||
|
This resulting gdis executable will be located in build/os/<platform>.
|
69
GPL/GnuDisassembler/build.gradle
Normal file
69
GPL/GnuDisassembler/build.gradle
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
// If extension module does not reside within the Ghidra GPL directory, the Ghidra installation directory
|
||||||
|
// must be specified either by setting the GHIDRA_INSTALL_DIR environment variable or Gradle
|
||||||
|
// project property:
|
||||||
|
//
|
||||||
|
// > export GHIDRA_INSTALL_DIR=<Absolute path to Ghidra>
|
||||||
|
// > gradle build
|
||||||
|
//
|
||||||
|
// or
|
||||||
|
//
|
||||||
|
// > gradle -PGHIDRA_INSTALL_DIR=<Absolute path to Ghidra> build
|
||||||
|
//
|
||||||
|
// In addition, the appropriate binutils source distribution archive must be placed
|
||||||
|
// within this module's directory (see below for binutils version and archive file naming.
|
||||||
|
//
|
||||||
|
// Gradle should be invoked from the directory of the extension module to build. Please see the
|
||||||
|
// application.gradle.version property in <GHIDRA_INSTALL_DIR>/Ghidra/application.properties
|
||||||
|
// for the correction version of Gradle to use for the Ghidra installation you specify.
|
||||||
|
|
||||||
|
ext.binutils = "binutils-2.29.1"
|
||||||
|
ext.binutilsDistro = "${binutils}.tar.bz2"
|
||||||
|
|
||||||
|
ext.ghidraInstallDir = null;
|
||||||
|
|
||||||
|
if (file("../gpl.gradle").exists()) {
|
||||||
|
// Module is located within the Ghidra GPL directory
|
||||||
|
ext.ghidraInstallDir = file("../..").getCanonicalPath()
|
||||||
|
ext.binutilsLocation = file("${ghidraInstallDir}/../ghidra.bin/GPL/${name}").getCanonicalPath()
|
||||||
|
apply from: file("../gpl.gradle").getCanonicalPath()
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// various module placements for Ghidra installations
|
||||||
|
ext.binutilsLocation = projectDir
|
||||||
|
if (file("../../../GPL/gpl.gradle").exists()) {
|
||||||
|
// Handle GPL extension install within Ghidra Extensions directory
|
||||||
|
ext.ghidraInstallDir = file("../../..").getCanonicalPath()
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// Handle extension install outside of Ghidra installation - must specify Ghidra install path
|
||||||
|
if (System.env.GHIDRA_INSTALL_DIR) {
|
||||||
|
ext.ghidraInstallDir = System.env.GHIDRA_INSTALL_DIR
|
||||||
|
}
|
||||||
|
else if (project.hasProperty("GHIDRA_INSTALL_DIR")) {
|
||||||
|
ext.ghidraInstallDir = project.getProperty("GHIDRA_INSTALL_DIR")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (ghidraInstallDir) {
|
||||||
|
if (ghidraInstallDir.replace("\\","/").endsWith("/")) {
|
||||||
|
ext.ghidraInstallDir = ghidraInstallDir.substring(0, ghidraInstallDir.length()-1)
|
||||||
|
}
|
||||||
|
println "Building with Ghidra installation at $ghidraInstallDir"
|
||||||
|
apply from: new File(ghidraInstallDir).getCanonicalPath() + "/GPL/gpl.gradle"
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
throw new GradleException("GHIDRA_INSTALL_DIR is not defined!")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (findProject(':Generic') != null) {
|
||||||
|
// Handle integrated Ghidra build - do not build gdis native
|
||||||
|
apply from: "$rootProject.projectDir/gradle/distributableGPLExtension.gradle"
|
||||||
|
delete file("build/os"); // remove any prior build of gdis
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
apply from: "${ghidraInstallDir}/GPL/nativeBuildProperties.gradle"
|
||||||
|
apply from: "buildGdis.gradle"
|
||||||
|
}
|
||||||
|
|
||||||
|
apply plugin: 'eclipse'
|
||||||
|
eclipse.project.name = 'Xtra GPL GnuDisassembler'
|
169
GPL/GnuDisassembler/buildGdis.gradle
Normal file
169
GPL/GnuDisassembler/buildGdis.gradle
Normal file
@ -0,0 +1,169 @@
|
|||||||
|
/*******************************************************************************************
|
||||||
|
* build.gradle file that applies this script must define two properties
|
||||||
|
* 1) binutilsLocation - the folder where the original binutils.zip lives
|
||||||
|
* 2) binutilsPrebuiltPath - the folder where the custom prebuilt binutils lives or will be built to
|
||||||
|
*******************************************************************************************/
|
||||||
|
|
||||||
|
defaultTasks 'assemble'
|
||||||
|
|
||||||
|
ext.supportedPlatforms = ['osx64', 'linux64']
|
||||||
|
|
||||||
|
ext.binutilsResource = new File("${binutilsLocation}/${binutils}.tar.bz2")
|
||||||
|
|
||||||
|
def binutilsUnpackDir = file("${project.buildDir}/${binutils}/")
|
||||||
|
|
||||||
|
/******************************************************************************************
|
||||||
|
*
|
||||||
|
* For each supported platform build the following tasks:
|
||||||
|
* buildBinutils_<platform> builds binutils for the platform
|
||||||
|
* packageBinutilsDev_<platform> creates the built bundle of stuf we need to build gdis
|
||||||
|
* unpackBinutilsPrebuilt_<platform> unpacks the built bundle to be used to build gdis
|
||||||
|
*
|
||||||
|
******************************************************************************************/
|
||||||
|
|
||||||
|
model {
|
||||||
|
platforms {
|
||||||
|
linux64 {
|
||||||
|
architecture 'x86_64'
|
||||||
|
operatingSystem 'linux'
|
||||||
|
}
|
||||||
|
osx64 {
|
||||||
|
architecture 'x86_64'
|
||||||
|
operatingSystem 'osx'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
components {
|
||||||
|
|
||||||
|
gdis(NativeExecutableSpec) {
|
||||||
|
|
||||||
|
// NOTE: Windows build requires Mingw and is very very slow and touchy
|
||||||
|
supportedPlatforms.each { targetPlatform it}
|
||||||
|
|
||||||
|
sources {
|
||||||
|
c {
|
||||||
|
source {
|
||||||
|
srcDir "src/gdis/c"
|
||||||
|
include "disasm_1.c"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
binaries {
|
||||||
|
all {
|
||||||
|
def binutilsArtifactsDir = file("build/binutils/${targetPlatform.name}")
|
||||||
|
if ((toolChain in Gcc) || (toolChain in Clang)) {
|
||||||
|
cCompiler.args "-I${binutilsArtifactsDir}/include", "-I${binutilsArtifactsDir}/bfd"
|
||||||
|
linker.args "-L${binutilsArtifactsDir}/lib", "-lopcodes", "-lbfd", "-liberty", "-lz", "-ldl"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tasks.compileGdisOsx64ExecutableGdisC {
|
||||||
|
dependsOn 'copyBinutilsArtifcats_osx64'
|
||||||
|
}
|
||||||
|
tasks.compileGdisLinux64ExecutableGdisC {
|
||||||
|
dependsOn 'copyBinutilsArtifcats_linux64'
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// change gdis linker output directory to build/os/<platform>
|
||||||
|
gradle.taskGraph.whenReady {
|
||||||
|
def p = this.project
|
||||||
|
p.tasks.withType(LinkExecutable).each { t ->
|
||||||
|
File f = t.linkedFile.getAsFile().get()
|
||||||
|
String filename = f.getName()
|
||||||
|
NativePlatform platform = t.targetPlatform.get()
|
||||||
|
String osName = platform.getName()
|
||||||
|
t.linkedFile = p.file("build/os/${osName}/$filename")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*******************************************************************************************
|
||||||
|
* Task to unpack the standard binutils zip file
|
||||||
|
*******************************************************************************************/
|
||||||
|
task binutilsUnpack {
|
||||||
|
description "Unpack binutils (for building gdis)"
|
||||||
|
group "Native Build Dependencies"
|
||||||
|
outputs.file { binutilsUnpackDir }
|
||||||
|
onlyIf { !binutilsUnpackDir.exists() }
|
||||||
|
|
||||||
|
doFirst {
|
||||||
|
if (!binutilsResource.exists()) {
|
||||||
|
throw new GradleException("${binutilsResource.getCanonicalPath()} not found")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
doLast {
|
||||||
|
copy {
|
||||||
|
from tarTree(resources.bzip2("${binutilsResource}"))
|
||||||
|
into file("build")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
supportedPlatforms.each { platform ->
|
||||||
|
|
||||||
|
def buildName = "buildBinutils_${platform}"
|
||||||
|
def postBuildName = "copyBinutilsArtifcats_${platform}"
|
||||||
|
|
||||||
|
def configDir = file("build/config/${platform}")
|
||||||
|
def artifactsDir = file("build/binutils/${platform}")
|
||||||
|
|
||||||
|
task(buildName) {
|
||||||
|
description "Configure and make binutils for $platform (for building gdis)"
|
||||||
|
group "Native Prebuild Dependencies"
|
||||||
|
|
||||||
|
onlyIf { !configDir.exists() }
|
||||||
|
|
||||||
|
dependsOn binutilsUnpack
|
||||||
|
|
||||||
|
inputs.dir binutilsUnpackDir
|
||||||
|
outputs.dir configDir
|
||||||
|
|
||||||
|
doLast {
|
||||||
|
|
||||||
|
File binutilsDir = binutilsUnpackDir
|
||||||
|
delete configDir
|
||||||
|
|
||||||
|
println "Configuring binutils - config directory: $configDir"
|
||||||
|
println "${binutilsDir}/configure --prefix=\"${configDir}\" --enable-targets=all --with-zlib=no --disable-nls --disable-werror"
|
||||||
|
configDir.mkdirs();
|
||||||
|
exec {
|
||||||
|
workingDir configDir
|
||||||
|
commandLine "${binutilsDir}/configure", "--prefix=${configDir}", "--enable-targets=all", "--with-zlib=no", "--disable-nls", "--disable-werror"
|
||||||
|
}
|
||||||
|
|
||||||
|
println "Building binutils - config directory: $configDir"
|
||||||
|
exec {
|
||||||
|
commandLine "make", "-C", "${configDir}", "all"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
task(postBuildName, type: Copy) {
|
||||||
|
description "Copy binutil artifcacts for $platform (for building gdis)"
|
||||||
|
group "Native Prebuild Dependencies"
|
||||||
|
|
||||||
|
dependsOn buildName
|
||||||
|
|
||||||
|
destinationDir = artifactsDir
|
||||||
|
|
||||||
|
into("/include") {
|
||||||
|
from("${binutilsUnpackDir}/include")
|
||||||
|
include "**/*.h"
|
||||||
|
}
|
||||||
|
into("/bfd") {
|
||||||
|
from "${configDir}/bfd"
|
||||||
|
include "**/*.h"
|
||||||
|
}
|
||||||
|
into("/lib") {
|
||||||
|
from "${configDir}/bfd/libbfd.a"
|
||||||
|
from "${configDir}/libiberty/libiberty.a"
|
||||||
|
from "${configDir}/opcodes/libopcodes.a"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
14
GPL/GnuDisassembler/certification.manifest
Normal file
14
GPL/GnuDisassembler/certification.manifest
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
##VERSION: 2.0
|
||||||
|
##MODULE IP: GPL 2
|
||||||
|
##MODULE IP: Public Domain
|
||||||
|
.classpath||GHIDRA||||END|
|
||||||
|
.project||GHIDRA||||END|
|
||||||
|
Module.manifest||Public Domain||||END|
|
||||||
|
README.txt||Public Domain||||END|
|
||||||
|
build.gradle||Public Domain||||END|
|
||||||
|
buildGdis.gradle||Public Domain||||END|
|
||||||
|
data/arm_test1.s||Public Domain||||END|
|
||||||
|
data/big.elf||Public Domain||||END|
|
||||||
|
data/little.elf||Public Domain||||END|
|
||||||
|
extension.properties||Public Domain||||END|
|
||||||
|
settings.gradle||Public Domain||||END|
|
7
GPL/GnuDisassembler/data/arm_test1.s
Normal file
7
GPL/GnuDisassembler/data/arm_test1.s
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
.text
|
||||||
|
__start:
|
||||||
|
lw $t0, #4
|
||||||
|
li $t1, #0
|
||||||
|
add $t2, $t0, $t1
|
||||||
|
done
|
||||||
|
|
BIN
GPL/GnuDisassembler/data/big.elf
Normal file
BIN
GPL/GnuDisassembler/data/big.elf
Normal file
Binary file not shown.
BIN
GPL/GnuDisassembler/data/little.elf
Normal file
BIN
GPL/GnuDisassembler/data/little.elf
Normal file
Binary file not shown.
6
GPL/GnuDisassembler/extension.properties
Normal file
6
GPL/GnuDisassembler/extension.properties
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
name=GnuDisassembler
|
||||||
|
description=GNU Disassembler. Extension is delivered unbuilt. See module README.txt for build instructions.
|
||||||
|
author=Ghidra Team
|
||||||
|
createdOn=6/18/2019
|
||||||
|
version=@extversion@
|
||||||
|
gpl=true
|
0
GPL/GnuDisassembler/settings.gradle
Normal file
0
GPL/GnuDisassembler/settings.gradle
Normal file
457
GPL/GnuDisassembler/src/gdis/c/disasm_1.c
Normal file
457
GPL/GnuDisassembler/src/gdis/c/disasm_1.c
Normal file
@ -0,0 +1,457 @@
|
|||||||
|
/* ###
|
||||||
|
* IP: GPL 2
|
||||||
|
*/
|
||||||
|
#include "config.h"
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdarg.h>
|
||||||
|
|
||||||
|
#include "bfd.h"
|
||||||
|
#include "dis-asm.h"
|
||||||
|
// #include "bucomm.h" // for set_default_bfd_target()
|
||||||
|
|
||||||
|
#include "gdis.h"
|
||||||
|
|
||||||
|
#define MAX_ASCII_CHAR_BYTE_STRING 256
|
||||||
|
|
||||||
|
|
||||||
|
void listSupportedArchMachTargets(void)
|
||||||
|
{
|
||||||
|
const char** targetList;
|
||||||
|
const char** archList;
|
||||||
|
int i, j;
|
||||||
|
|
||||||
|
targetList = bfd_target_list();
|
||||||
|
if(targetList != NULL){
|
||||||
|
for(i=0, j=0; targetList[i] !=0; i++){
|
||||||
|
printf("Supported Target: %s\n", targetList[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
printf("\ndone with targetList.\n");
|
||||||
|
|
||||||
|
archList = bfd_arch_list();
|
||||||
|
if(archList != NULL){
|
||||||
|
for(i=0, j=0; archList[i] !=0; i++){
|
||||||
|
printf("Supported Arch: %s\n", archList[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
printf("\ndone with archList.\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/* sprintf to a "stream". */
|
||||||
|
int objdump_sprintf (SFILE *f, const char *format, ...)
|
||||||
|
{
|
||||||
|
|
||||||
|
int i;
|
||||||
|
size_t n;
|
||||||
|
va_list args;
|
||||||
|
|
||||||
|
va_start (args, format);
|
||||||
|
n = vsnprintf (f->buffer + f->pos, BUFF_SIZE, format, args);
|
||||||
|
strncat(disassembled_buffer, f->buffer, n);
|
||||||
|
va_end (args);
|
||||||
|
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void configureDisassembleInfo(bfd* abfd,
|
||||||
|
disassemble_info* info,
|
||||||
|
enum bfd_architecture arch,
|
||||||
|
unsigned long mach,
|
||||||
|
enum bfd_endian end)
|
||||||
|
{
|
||||||
|
|
||||||
|
memset(sfile.buffer, 0x00, BUFF_SIZE);
|
||||||
|
|
||||||
|
INIT_DISASSEMBLE_INFO(*info, stdout, objdump_sprintf);
|
||||||
|
info->arch = (enum bfd_architecture) arch;
|
||||||
|
info->mach = mach;
|
||||||
|
info->flavour = bfd_get_flavour(abfd);
|
||||||
|
info->endian = end;
|
||||||
|
info->stream = (FILE*)&sfile; // set up our "buffer stream"
|
||||||
|
info->display_endian = BFD_ENDIAN_LITTLE;
|
||||||
|
/* Allow the target to customize the info structure. */
|
||||||
|
disassemble_init_for_target(info);
|
||||||
|
}
|
||||||
|
|
||||||
|
disassembler_ftype configureBfd(bfd* abfd,
|
||||||
|
enum bfd_architecture arch,
|
||||||
|
unsigned long mach,
|
||||||
|
enum bfd_endian endian,
|
||||||
|
disassemble_info* DI,
|
||||||
|
disassembler_ftype* disassemble_fn)
|
||||||
|
{
|
||||||
|
struct bfd_target *xvec;
|
||||||
|
|
||||||
|
abfd->flags |= EXEC_P;
|
||||||
|
|
||||||
|
|
||||||
|
// set up xvec byteorder.
|
||||||
|
xvec = (struct bfd_target *) malloc (sizeof (struct bfd_target));
|
||||||
|
memset(xvec, 0x00, sizeof (struct bfd_target));
|
||||||
|
memcpy (xvec, abfd->xvec, sizeof (struct bfd_target));
|
||||||
|
xvec->byteorder = endian;
|
||||||
|
abfd->xvec = xvec;
|
||||||
|
|
||||||
|
configureDisassembleInfo(abfd, DI, arch, mach, endian);
|
||||||
|
if(endian == BFD_ENDIAN_BIG){
|
||||||
|
bfd_big_endian(abfd);
|
||||||
|
DI->display_endian = DI->endian = BFD_ENDIAN_BIG;
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
bfd_little_endian(abfd);
|
||||||
|
DI->display_endian = DI->endian = BFD_ENDIAN_LITTLE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
bfd_error_type err = bfd_get_error();
|
||||||
|
printf("bfd_error_msg: %s.\n", bfd_errmsg(err));
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* Use libopcodes to locate a suitable disassembler. */
|
||||||
|
*disassemble_fn = NULL;
|
||||||
|
*disassemble_fn = disassembler (arch, endian == BFD_ENDIAN_BIG, mach, abfd);
|
||||||
|
if (!*disassemble_fn){
|
||||||
|
printf("can't disassemble for arch 0x%08X, mach 0x%08lX\n", arch, mach);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
return *disassemble_fn;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
int disassemble_buffer( disassembler_ftype disassemble_fn,
|
||||||
|
disassemble_info *info,
|
||||||
|
int* offset,
|
||||||
|
PDIS_INFO pDisInfo)
|
||||||
|
{
|
||||||
|
int i, j, size = 0;
|
||||||
|
int len = 0;
|
||||||
|
|
||||||
|
while ( *offset < info->buffer_length ) {
|
||||||
|
/* call the libopcodes disassembler */
|
||||||
|
memset(pDisInfo->disassemblyString, 0x00, MAX_DIS_STRING);
|
||||||
|
|
||||||
|
/* set the insn_info_valid bit to 0, as explained in BFD's
|
||||||
|
* include/dis-asm.h. The bit will then be set to tell us
|
||||||
|
* whether the decoder supports "extra" information about the
|
||||||
|
* instruction.
|
||||||
|
*/
|
||||||
|
info->insn_info_valid = 0;
|
||||||
|
|
||||||
|
size = (*disassemble_fn)(info->buffer_vma + *offset, info);
|
||||||
|
/* -- analyze disassembled instruction here -- */
|
||||||
|
/* -- print any symbol names as labels here -- */
|
||||||
|
|
||||||
|
/* save off corresponding hex bytes */
|
||||||
|
for ( j= 0,i = 0; i < 8; i++, j+=3) {
|
||||||
|
if ( i < size ){
|
||||||
|
sprintf(&(pDisInfo->bytesBufferAscii[j]), "%02X ", info->buffer[*offset + i]);
|
||||||
|
pDisInfo->bytesBufferBin[i] = info->buffer[*offset + i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* add the augmented information to our disassembly info struct */
|
||||||
|
pDisInfo->count = size;
|
||||||
|
pDisInfo->insn_info_valid = info->insn_info_valid;
|
||||||
|
pDisInfo->branch_delay_insns = info->branch_delay_insns;
|
||||||
|
pDisInfo->data_size = info->data_size;
|
||||||
|
pDisInfo->insn_type = info->insn_type;
|
||||||
|
pDisInfo->target = info->target;
|
||||||
|
pDisInfo->target2 = info->target2;
|
||||||
|
|
||||||
|
strcat(&(pDisInfo->disassemblyString[0]), disassembled_buffer);
|
||||||
|
memset(disassembled_buffer, 0x00, BUFF_SIZE);
|
||||||
|
|
||||||
|
if(size != 0){
|
||||||
|
*offset += size; /* advance position in buffer */
|
||||||
|
goto END;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
END:
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
|
||||||
|
void processBuffer(unsigned char* buff,
|
||||||
|
int buff_len,
|
||||||
|
bfd_vma buff_vma,
|
||||||
|
disassembler_ftype disassemble_fn,
|
||||||
|
struct disassemble_info* DI)
|
||||||
|
{
|
||||||
|
int bytesConsumed = -1;
|
||||||
|
int offset = 0;
|
||||||
|
int numDisassemblies = 0;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
DI->buffer = buff; /* buffer of bytes to disassemble */
|
||||||
|
DI->buffer_length = buff_len; /* size of buffer */
|
||||||
|
DI->buffer_vma = buff_vma; /* base RVA of buffer */
|
||||||
|
|
||||||
|
memset(disassemblyInfoBuffer, 0x00, sizeof(DIS_INFO)*MAX_NUM_ENTRIES);
|
||||||
|
|
||||||
|
while((buff_len - offset) > 0 && bytesConsumed != 0 && numDisassemblies < MAX_NUM_ENTRIES){
|
||||||
|
bytesConsumed = disassemble_buffer( disassemble_fn, DI, &offset, &(disassemblyInfoBuffer[numDisassemblies++]));
|
||||||
|
}
|
||||||
|
for (i = 0; i < numDisassemblies; i++) {
|
||||||
|
printf("%s\nInfo: %d,%d,%d,%d,%d\n", disassemblyInfoBuffer[i].disassemblyString,
|
||||||
|
disassemblyInfoBuffer[i].count,
|
||||||
|
disassemblyInfoBuffer[i].insn_info_valid,
|
||||||
|
disassemblyInfoBuffer[i].branch_delay_insns,
|
||||||
|
disassemblyInfoBuffer[i].data_size,
|
||||||
|
disassemblyInfoBuffer[i].insn_type);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char* argv[]){
|
||||||
|
struct disassemble_info DI;
|
||||||
|
enum bfd_architecture arch;
|
||||||
|
struct bfd_arch_info ai;
|
||||||
|
unsigned long mach;
|
||||||
|
enum bfd_endian endian;
|
||||||
|
unsigned int end;
|
||||||
|
bfd_vma offset;
|
||||||
|
disassembler_ftype disassemble_fn;
|
||||||
|
char *target = default_target;
|
||||||
|
bfd *bfdfile;
|
||||||
|
unsigned long a,m;
|
||||||
|
char* byteString;
|
||||||
|
char elf_file_location[MAX_ELF_FILE_PATH_LEN];
|
||||||
|
char arch_str[256];
|
||||||
|
char mach_str[256];
|
||||||
|
|
||||||
|
if ( argc < 8) {
|
||||||
|
fprintf(stderr, "Usage: %s target-str, arch, mach, disassembly base-addr (for rel offsets instrs), full-path to Little and Big Elfs, big/little ascii-byte-string up to %d chars\n", argv[0], MAX_ASCII_CHAR_BYTE_STRING);
|
||||||
|
listSupportedArchMachTargets();
|
||||||
|
const char** archList = bfd_arch_list();
|
||||||
|
const bfd_arch_info_type* ait;
|
||||||
|
while(*archList != NULL){
|
||||||
|
printf("checking against architecture: %s.\n", *archList);
|
||||||
|
ait = NULL;
|
||||||
|
ait = bfd_scan_arch(*archList);
|
||||||
|
if(ait != NULL){
|
||||||
|
printf("archname: %s arch: 0x%08X, mach: 0x%08lX.\n", ait->arch_name, ait->arch, ait->mach);
|
||||||
|
}
|
||||||
|
archList++;
|
||||||
|
}
|
||||||
|
return(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
end = 0x00000000;
|
||||||
|
endian = (enum bfd_endian) 0x00;
|
||||||
|
mach = 0x00000000;
|
||||||
|
arch = (enum bfd_architecture) 0x00;
|
||||||
|
offset = 0x00000000;
|
||||||
|
|
||||||
|
sscanf(argv[2], "%128s", arch_str);
|
||||||
|
sscanf(argv[3], "%18lX", &mach);
|
||||||
|
sscanf(argv[4], "%10X", &end);
|
||||||
|
sscanf(argv[5], "%18lX", &offset);
|
||||||
|
|
||||||
|
// if arch starts with 0x, then parse a number
|
||||||
|
// else lookup the string in the table to get the arch, ignore the mach
|
||||||
|
if (arch_str[0] == '0' && arch_str[1] == 'x') {
|
||||||
|
sscanf(arch_str, "%10X", &arch);
|
||||||
|
} else {
|
||||||
|
const char** archList = bfd_arch_list();
|
||||||
|
const bfd_arch_info_type* ait;
|
||||||
|
while(*archList != NULL){
|
||||||
|
ait = bfd_scan_arch(*archList);
|
||||||
|
if(strcmp(arch_str, *archList)== 0){
|
||||||
|
arch = ait->arch;
|
||||||
|
mach = ait->mach;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
ait = NULL;
|
||||||
|
archList++;
|
||||||
|
}
|
||||||
|
if (ait == NULL) {
|
||||||
|
printf("Couldn't find arch %s\n", arch_str);
|
||||||
|
return(-1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
endian = (enum bfd_endian) end;
|
||||||
|
/* open a correct type of file to fill in most of the required data. */
|
||||||
|
|
||||||
|
// printf("Arch is: 0x%08X, Machine is: 0x%08lX Endian is: 0x%02X.\n", arch, mach, endian);
|
||||||
|
|
||||||
|
memset(elf_file_location, 0x00, MAX_ELF_FILE_PATH_LEN);
|
||||||
|
strncpy(elf_file_location, argv[6], MAX_ELF_FILE_PATH_LEN-sizeof(LITTLE_ELF_FILE)-2); // actual file name and nulls
|
||||||
|
|
||||||
|
// arg[7] is either a hex string or the string "stdin", which
|
||||||
|
// triggers reading line by line from stdin.
|
||||||
|
|
||||||
|
byteString = argv[7];
|
||||||
|
int stdin_mode = 2; // use CLI
|
||||||
|
if (strcmp(byteString, "stdin") == 0) {
|
||||||
|
stdin_mode = 1; // use STDIN
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned char byteBuffer[BYTE_BUFFER_SIZE];
|
||||||
|
char byteStringBuffer[(BYTE_BUFFER_SIZE*2)];
|
||||||
|
|
||||||
|
char addressStringBuffer[128];
|
||||||
|
|
||||||
|
if (endian == BFD_ENDIAN_BIG){
|
||||||
|
strcat(elf_file_location, BIG_ELF_FILE);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
strcat(elf_file_location, LITTLE_ELF_FILE);
|
||||||
|
}
|
||||||
|
|
||||||
|
while (stdin_mode) {
|
||||||
|
|
||||||
|
// convert user input AsciiHex to Binary data for processing
|
||||||
|
char tmp[3];
|
||||||
|
unsigned int byteValue;
|
||||||
|
tmp[0] = tmp[1] = tmp[2] = 0x00;
|
||||||
|
|
||||||
|
if (stdin_mode == 1) { // use stdin
|
||||||
|
// read in the address
|
||||||
|
if (fgets(addressStringBuffer, sizeof(addressStringBuffer), stdin)) {
|
||||||
|
|
||||||
|
//fprintf(stderr, "read: %s\n", addressStringBuffer);
|
||||||
|
//char *p = strchr(addressStringBuffer, '\n');
|
||||||
|
//if (p) {
|
||||||
|
// *p = '\0';
|
||||||
|
//}
|
||||||
|
|
||||||
|
sscanf(addressStringBuffer, "%18lX", &offset);
|
||||||
|
}
|
||||||
|
//getchar();
|
||||||
|
// read in the ASCII hex string from stdin
|
||||||
|
if (fgets(byteStringBuffer, sizeof(byteStringBuffer), stdin)) {
|
||||||
|
|
||||||
|
//fprintf(stderr, "read: %s\n", byteStringBuffer);
|
||||||
|
// remove trailing newline
|
||||||
|
char *p = strchr(byteStringBuffer, '\n');
|
||||||
|
if (p) {
|
||||||
|
*p = '\0';
|
||||||
|
}
|
||||||
|
//if (strcmp(byteStringBuffer, "EOF") == 0) {
|
||||||
|
//return 0; // terminate on EOF string
|
||||||
|
//}
|
||||||
|
} else {
|
||||||
|
fprintf(stderr, "exiting, no ASCII hex found\n");
|
||||||
|
return 0; // finished! #TODO
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
if(strlen(byteString) > BYTE_BUFFER_SIZE*2) {
|
||||||
|
fprintf(stderr, "Max ascii string size is %d you provided: %lu chars. Exiting.\n", BYTE_BUFFER_SIZE*2,
|
||||||
|
strlen(byteString));
|
||||||
|
exit(-1);
|
||||||
|
}
|
||||||
|
strncpy(byteStringBuffer, byteString, BYTE_BUFFER_SIZE*2);
|
||||||
|
stdin_mode = 0; // break out of the while loop
|
||||||
|
}
|
||||||
|
|
||||||
|
int size = strlen(byteStringBuffer);
|
||||||
|
if((size % 2) != 0){
|
||||||
|
fprintf(stderr, "need even-number of ascii chars for byte-stream: (offset: %08lx, %s, %ld)\n", offset, byteStringBuffer, strlen(byteStringBuffer));
|
||||||
|
exit(-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
memset(byteBuffer, 0x00, BYTE_BUFFER_SIZE);
|
||||||
|
|
||||||
|
//
|
||||||
|
// TODO:
|
||||||
|
// check to make sure chars are only valid HEX.
|
||||||
|
//
|
||||||
|
int i, j;
|
||||||
|
for(i=j=0; (i < size) && (j < BYTE_BUFFER_SIZE); i+=2, j++){
|
||||||
|
tmp[0] = byteStringBuffer[i];
|
||||||
|
tmp[1] = byteStringBuffer[i+1];
|
||||||
|
tmp[2] = 0;
|
||||||
|
sscanf(tmp, "%02X", &byteValue);
|
||||||
|
byteBuffer[j] = (unsigned char)byteValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
for(j=0; j < BYTE_BUFFER_SIZE; j++){
|
||||||
|
printf("0x%02X ", byteBuffer[j]);
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
bfd_init( );
|
||||||
|
target = argv[1];
|
||||||
|
bfd_set_default_target(target);
|
||||||
|
|
||||||
|
// printf("Debug: BFD sample file: %s\n", elf_file_location);
|
||||||
|
// printf("Debug: LITTLE: %s\n", LITTLE_ELF_FILE);
|
||||||
|
// printf("Debug: BIG: %s\n", BIG_ELF_FILE);
|
||||||
|
|
||||||
|
if(endian == BFD_ENDIAN_BIG){
|
||||||
|
bfdfile = bfd_openr(elf_file_location, target );
|
||||||
|
if ( ! bfdfile ) {
|
||||||
|
printf("Error opening BIG ELF file: %s\n", elf_file_location);
|
||||||
|
bfd_perror( "Error on bfdfile" );
|
||||||
|
return(3);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
bfdfile = bfd_openr(elf_file_location, target );
|
||||||
|
if ( ! bfdfile ) {
|
||||||
|
printf("Error opening LITTLE ELF file: %s\n", elf_file_location);
|
||||||
|
// bfdfile = bfd_openr(elf_file_location, target );
|
||||||
|
bfd_perror( "Error on bfdfile" );
|
||||||
|
return(3);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
memset((void*) &DI, 0x00, sizeof(struct disassemble_info));
|
||||||
|
|
||||||
|
disassemble_fn = NULL;
|
||||||
|
|
||||||
|
// important set up!
|
||||||
|
//---------------------------------------
|
||||||
|
ai.arch = arch;
|
||||||
|
ai.mach = mach;
|
||||||
|
bfd_set_arch_info(bfdfile, &ai);
|
||||||
|
//---------------------------------------
|
||||||
|
|
||||||
|
/*
|
||||||
|
bfd_error_type err = bfd_get_error();
|
||||||
|
printf("bfd_error_msg: %s.\n", bfd_errmsg(err));
|
||||||
|
*/
|
||||||
|
|
||||||
|
configureBfd(bfdfile, arch, mach, endian, &DI, &disassemble_fn);
|
||||||
|
|
||||||
|
/*
|
||||||
|
err = bfd_get_error();
|
||||||
|
printf("bfd_error_msg: %s.\n", bfd_errmsg(err));
|
||||||
|
*/
|
||||||
|
|
||||||
|
if (disassemble_fn == NULL){
|
||||||
|
fprintf(stderr, "Error: disassemble_fn is NULL. Nothing I can do.\n");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
/*
|
||||||
|
printf("the disassemble_fn func pointer is: 0x%08X.\n", disassemble_fn);
|
||||||
|
printf("We can try to disassemble for this arch/mach. calling disassemble_init_for_target().\n");
|
||||||
|
*/
|
||||||
|
disassemble_init_for_target(&DI);
|
||||||
|
|
||||||
|
// go diassemble the buffer and build up the result in a accumulator string buffer.
|
||||||
|
processBuffer(byteBuffer, size >> 1, offset, disassemble_fn, &DI); //
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
free((void*)bfdfile->xvec);
|
||||||
|
bfd_close(bfdfile);
|
||||||
|
|
||||||
|
printf("EOF\n");
|
||||||
|
fflush(stdout);
|
||||||
|
|
||||||
|
} // while loop on lines of stdin
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
94
GPL/GnuDisassembler/src/gdis/c/gdis.h
Normal file
94
GPL/GnuDisassembler/src/gdis/c/gdis.h
Normal file
@ -0,0 +1,94 @@
|
|||||||
|
/* ###
|
||||||
|
* IP: GPL 2
|
||||||
|
*/
|
||||||
|
#ifndef _GDIS_H_
|
||||||
|
#define _GDIS_H_
|
||||||
|
|
||||||
|
#define BYTE_BUFFER_SIZE 128
|
||||||
|
|
||||||
|
#define LITTLE_ELF_FILE "little.elf" // built for intel x64
|
||||||
|
#define BIG_ELF_FILE "big.elf"
|
||||||
|
|
||||||
|
#define BUFF_SIZE 128
|
||||||
|
|
||||||
|
#define MAX_DIS_STRING 128
|
||||||
|
#define MAX_BYTES_STRING 64
|
||||||
|
#define MAX_BYTES 64
|
||||||
|
#define MAX_NUM_ENTRIES 64
|
||||||
|
#define MAX_ELF_FILE_PATH_LEN 512
|
||||||
|
|
||||||
|
|
||||||
|
typedef struct _DIS_INFO_{
|
||||||
|
char disassemblyString[MAX_DIS_STRING];
|
||||||
|
char bytesBufferAscii[MAX_BYTES_STRING];
|
||||||
|
unsigned char bytesBufferBin[MAX_BYTES];
|
||||||
|
|
||||||
|
int count; /* Number of bytes consumed */
|
||||||
|
|
||||||
|
char insn_info_valid; /* Branch info has been set. */
|
||||||
|
char branch_delay_insns; /* How many sequential insn's will run before
|
||||||
|
a branch takes effect. (0 = normal) */
|
||||||
|
char data_size; /* Size of data reference in insn, in bytes */
|
||||||
|
enum dis_insn_type insn_type; /* Type of instruction */
|
||||||
|
bfd_vma target; /* Target address of branch or dref, if known;
|
||||||
|
zero if unknown. */
|
||||||
|
bfd_vma target2; /* Second target address for dref2 */
|
||||||
|
|
||||||
|
} DIS_INFO, *PDIS_INFO;
|
||||||
|
|
||||||
|
static DIS_INFO disassemblyInfoBuffer[MAX_NUM_ENTRIES];
|
||||||
|
|
||||||
|
char mnemonic[32] = {0}, src[32] = {0}, dest[32] = {0}, arg[32] = {0};
|
||||||
|
char disassembled_buffer[BUFF_SIZE];
|
||||||
|
|
||||||
|
|
||||||
|
/* Pseudo FILE object for strings. */
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
// char *buffer;
|
||||||
|
char buffer[BUFF_SIZE];
|
||||||
|
size_t pos;
|
||||||
|
size_t alloc;
|
||||||
|
} SFILE;
|
||||||
|
|
||||||
|
|
||||||
|
static SFILE sfile;
|
||||||
|
|
||||||
|
static char *default_target = NULL; /* Default at runtime. */
|
||||||
|
|
||||||
|
// ------------------------------------------------------------------------
|
||||||
|
|
||||||
|
void listSupportedArchMachTargets(void);
|
||||||
|
|
||||||
|
int objdump_sprintf (SFILE *f, const char *format, ...);
|
||||||
|
|
||||||
|
void configureDisassembleInfo(bfd* abfd,
|
||||||
|
disassemble_info* info,
|
||||||
|
enum bfd_architecture arch,
|
||||||
|
unsigned long mach,
|
||||||
|
enum bfd_endian end);
|
||||||
|
|
||||||
|
disassembler_ftype configureBfd(bfd* abfd,
|
||||||
|
enum bfd_architecture arch,
|
||||||
|
unsigned long mach,
|
||||||
|
enum bfd_endian endian,
|
||||||
|
disassemble_info* DI,
|
||||||
|
disassembler_ftype* disassemble_fn);
|
||||||
|
|
||||||
|
int disassemble_buffer( disassembler_ftype disassemble_fn,
|
||||||
|
disassemble_info *info,
|
||||||
|
int* offset,
|
||||||
|
PDIS_INFO pDisInfo);
|
||||||
|
|
||||||
|
void processBuffer(unsigned char* buff,
|
||||||
|
int buff_len,
|
||||||
|
bfd_vma buff_vma,
|
||||||
|
disassembler_ftype disassemble_fn,
|
||||||
|
struct disassemble_info* DI);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#endif
|
@ -1,5 +1,4 @@
|
|||||||
##VERSION: 2.0
|
##VERSION: 2.0
|
||||||
##MODULE IP: Public Domain
|
##MODULE IP: Public Domain
|
||||||
build.gradle||Public Domain||||END|
|
gpl.gradle||Public Domain||||END|
|
||||||
nativeBuildProperties.gradle||Public Domain||||END|
|
nativeBuildProperties.gradle||Public Domain||||END|
|
||||||
settings.gradle||Public Domain||||END|
|
|
||||||
|
@ -1,11 +1,13 @@
|
|||||||
project.ext.BIN_REPO = file("${projectDir}/../../ghidra.bin").absolutePath
|
|
||||||
|
// BIN_REPO only useable in full Ghidra source configuration
|
||||||
|
project.ext.BIN_REPO = file("../../../ghidra.bin").absolutePath
|
||||||
|
|
||||||
project.ext.set("OS_NAMES", ["osx64", "win32", "win64", "linux64"])
|
project.ext.set("OS_NAMES", ["osx64", "win32", "win64", "linux64"])
|
||||||
|
|
||||||
/*********************************************************************************
|
/*********************************************************************************
|
||||||
* Returns the local platform name.
|
* Returns the local platform name.
|
||||||
*********************************************************************************/
|
*********************************************************************************/
|
||||||
String getCurrentPlatformName() {
|
ext.getCurrentPlatformName = {
|
||||||
|
|
||||||
String osName = System.getProperty("os.name")
|
String osName = System.getProperty("os.name")
|
||||||
String archName = System.getProperty("os.arch")
|
String archName = System.getProperty("os.arch")
|
||||||
@ -37,10 +39,13 @@ String getCurrentPlatformName() {
|
|||||||
* Helper method that returns a file that is the same relative location in the bin repo
|
* Helper method that returns a file that is the same relative location in the bin repo
|
||||||
* as the given project is in its repo.
|
* as the given project is in its repo.
|
||||||
******************************************************************************************/
|
******************************************************************************************/
|
||||||
File getProjectLocationInBinRepo(Project p) {
|
ext.getProjectLocationInBinRepo = {
|
||||||
String relativePath = getGhidraRelativePath(p)
|
String relativePath = getGhidraRelativePath(this.project)
|
||||||
|
println("RELATIVE: $relativePath")
|
||||||
File binRepoRootProject = new File("${BIN_REPO}")
|
File binRepoRootProject = new File("${BIN_REPO}")
|
||||||
|
if (!binRepoRootProject.isDirectory()) {
|
||||||
|
throw new GradleException("Task requires Ghidra source and ghidra.bin")
|
||||||
|
}
|
||||||
return new File(binRepoRootProject, relativePath)
|
return new File(binRepoRootProject, relativePath)
|
||||||
}
|
}
|
||||||
/****************************************************************************************
|
/****************************************************************************************
|
@ -1,4 +0,0 @@
|
|||||||
|
|
||||||
include "DemanglerGnu"
|
|
||||||
include "DMG"
|
|
||||||
include "CabExtract"
|
|
0
Ghidra/Extensions/SleighDevTools/Module.manifest
Normal file
0
Ghidra/Extensions/SleighDevTools/Module.manifest
Normal file
9
Ghidra/Extensions/SleighDevTools/build.gradle
Normal file
9
Ghidra/Extensions/SleighDevTools/build.gradle
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
apply from: "$rootProject.projectDir/gradle/distributableGhidraExtension.gradle"
|
||||||
|
apply from: "$rootProject.projectDir/gradle/javaProject.gradle"
|
||||||
|
apply from: "$rootProject.projectDir/gradle/javaTestProject.gradle"
|
||||||
|
apply plugin: 'eclipse'
|
||||||
|
eclipse.project.name = 'Xtra SleighDevTools'
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
compile project(':Base')
|
||||||
|
}
|
8
Ghidra/Extensions/SleighDevTools/certification.manifest
Normal file
8
Ghidra/Extensions/SleighDevTools/certification.manifest
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
##VERSION: 2.0
|
||||||
|
.classpath||GHIDRA||||END|
|
||||||
|
.project||GHIDRA||||END|
|
||||||
|
Module.manifest||GHIDRA||||END|
|
||||||
|
build.gradle||GHIDRA||||END|
|
||||||
|
data/ExtensionPoint.manifest||GHIDRA||||END|
|
||||||
|
data/LanguageMap.txt||GHIDRA||||END|
|
||||||
|
extension.properties||GHIDRA||||END|
|
@ -0,0 +1 @@
|
|||||||
|
ExternalDisassembler
|
13
Ghidra/Extensions/SleighDevTools/data/LanguageMap.txt
Normal file
13
Ghidra/Extensions/SleighDevTools/data/LanguageMap.txt
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
// Format: LanguageID#CustomGDISExecutable
|
||||||
|
//
|
||||||
|
// Mapping of LanguageNameFromGhidra to external (gdis) architecture names is no longer done here.
|
||||||
|
// This functionality has been moved to each language's ldefs file.
|
||||||
|
// External names are mapped via 'external_name' tags in language definitions.
|
||||||
|
// The CustomGDISExecutable is found via a call to Application.getOSFile(), which will search in
|
||||||
|
// the platform-specific OS directory within all modules.
|
||||||
|
//
|
||||||
|
// Lines starting with "//" are not parsed.
|
||||||
|
//
|
||||||
|
// '*' can be used to wild-card parts of the languageID
|
||||||
|
//
|
||||||
|
|
5
Ghidra/Extensions/SleighDevTools/extension.properties
Normal file
5
Ghidra/Extensions/SleighDevTools/extension.properties
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
name=SleighDevTools
|
||||||
|
description=Sleigh language development tools including external disassembler capabilities. The GnuDisassembler extension may be also be required as a disassembly provider.
|
||||||
|
author=Ghidra Team
|
||||||
|
createdOn=6/18/2019
|
||||||
|
version=@extversion@
|
@ -0,0 +1,320 @@
|
|||||||
|
/* ###
|
||||||
|
* IP: GHIDRA
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
// Compare Sliegh disassembly with external disassembly results
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import ghidra.app.script.GhidraScript;
|
||||||
|
import ghidra.app.util.PseudoDisassembler;
|
||||||
|
import ghidra.app.util.PseudoInstruction;
|
||||||
|
import ghidra.app.util.disassemble.GNUExternalDisassembler;
|
||||||
|
import ghidra.program.disassemble.Disassembler;
|
||||||
|
import ghidra.program.model.address.*;
|
||||||
|
import ghidra.program.model.lang.Register;
|
||||||
|
import ghidra.program.model.lang.UnknownInstructionException;
|
||||||
|
import ghidra.program.model.listing.BookmarkType;
|
||||||
|
import ghidra.program.model.listing.CodeUnit;
|
||||||
|
import ghidra.program.model.scalar.Scalar;
|
||||||
|
import ghidra.util.exception.CancelledException;
|
||||||
|
|
||||||
|
public class CompareSleighExternal extends GhidraScript {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void run() throws Exception {
|
||||||
|
if (currentProgram == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
AddressSetView set = currentSelection;
|
||||||
|
if (set == null || set.isEmpty()) {
|
||||||
|
set = currentProgram.getMemory().getLoadedAndInitializedAddressSet();
|
||||||
|
}
|
||||||
|
|
||||||
|
putEquivalent("xzr", "x31"); // Think they messed up and allowed x31, there is no x31
|
||||||
|
putEquivalent("wzr", "w31"); // Think they messed up and allowed w31, there is no w31
|
||||||
|
putEquivalent("r12", "ip");
|
||||||
|
|
||||||
|
int completed = 0;
|
||||||
|
monitor.initialize(set.getNumAddresses());
|
||||||
|
|
||||||
|
AddressIterator addresses = set.getAddresses(true);
|
||||||
|
|
||||||
|
PseudoDisassembler pseudoDisassembler = new PseudoDisassembler(currentProgram);
|
||||||
|
|
||||||
|
GNUExternalDisassembler dis = new GNUExternalDisassembler();
|
||||||
|
|
||||||
|
long align = currentProgram.getLanguage().getInstructionAlignment();
|
||||||
|
while (addresses.hasNext()) {
|
||||||
|
monitor.checkCanceled();
|
||||||
|
Address addr = addresses.next();
|
||||||
|
|
||||||
|
completed++;
|
||||||
|
|
||||||
|
// only on valid boundaries
|
||||||
|
if ((addr.getOffset() % align) != 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
clearBad(addr);
|
||||||
|
|
||||||
|
monitor.setProgress(completed);
|
||||||
|
|
||||||
|
CodeUnit cu = currentProgram.getListing().getCodeUnitAt(addr);
|
||||||
|
if (cu == null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
String str = dis.getDisassembly(cu);
|
||||||
|
|
||||||
|
str = str.toLowerCase();
|
||||||
|
|
||||||
|
PseudoInstruction pinst = null;
|
||||||
|
try {
|
||||||
|
pinst = pseudoDisassembler.disassemble(addr);
|
||||||
|
} catch (UnknownInstructionException e) {
|
||||||
|
// didn't get an instruction, did external not get one?
|
||||||
|
if (str.startsWith(".inst") && str.endsWith("undefined")) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
markErrorBad(addr,"Unimplemented Instruction", str);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// didn't get an instruction, did external not get one?
|
||||||
|
if (pinst == null && str.startsWith(".inst") && str.endsWith("undefined")) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pinst == null) {
|
||||||
|
markErrorBad(addr,"Unimplemented Instruction", str);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// collapse both instruction to strings, compare removing whitespace, and to-lower
|
||||||
|
String pStr = pinst.toString().toLowerCase().replaceAll("\\s","");
|
||||||
|
String eStr = str.toLowerCase().replaceAll("\\s", "");
|
||||||
|
|
||||||
|
// simple equivalence
|
||||||
|
if (pStr.equals(eStr)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
String mnemonic = pinst.getMnemonicString().toLowerCase();
|
||||||
|
if (!str.startsWith(mnemonic)) {
|
||||||
|
markBad(addr,"Mnemonic Disagreement", str + " != " + mnemonic);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
int start = str.indexOf(" ");
|
||||||
|
|
||||||
|
for (int opIndex = 0; opIndex < pinst.getNumOperands(); opIndex++) {
|
||||||
|
// try to parse the operand string from the instruction
|
||||||
|
int sepEnd = str.indexOf(",", start);
|
||||||
|
|
||||||
|
String extOp = getExtOpStr(str, start, sepEnd);
|
||||||
|
start = sepEnd + 1;
|
||||||
|
|
||||||
|
String valStr = null;
|
||||||
|
|
||||||
|
// TODO: could remove all characters, making sure none are left!
|
||||||
|
int loc = 0;
|
||||||
|
boolean subRegList = false;
|
||||||
|
List<Object> opObjList = pinst.getDefaultOperandRepresentationList(opIndex);
|
||||||
|
for (Object object : opObjList) {
|
||||||
|
if (object instanceof Character) {
|
||||||
|
Character ch = (Character) object;
|
||||||
|
ch = Character.toLowerCase(ch);
|
||||||
|
loc = extOp.indexOf(ch);
|
||||||
|
if (loc != -1) {
|
||||||
|
extOp = extOp.substring(0,loc) + extOp.substring(loc+1);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (ch.equals(',')) {
|
||||||
|
if (subRegList) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// gotta move into next string, must be embedded comma
|
||||||
|
sepEnd = str.indexOf(",", start);
|
||||||
|
|
||||||
|
extOp = getExtOpStr(str, start, sepEnd);
|
||||||
|
start = sepEnd + 1;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (ch.equals(' ')) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
markBad(addr,"Missing String Markup", ch.toString());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (object instanceof Scalar) {
|
||||||
|
// find the scalar, hex or decimal
|
||||||
|
Scalar scalar = (Scalar) object;
|
||||||
|
valStr = scalar.toString(16, false, false, "0x", "");
|
||||||
|
loc = extOp.indexOf(valStr);
|
||||||
|
if (loc != -1) {
|
||||||
|
extOp = extOp.substring(0,loc) + extOp.substring(loc+valStr.length());
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
valStr = scalar.toString(16, true, false, "0x", "");
|
||||||
|
loc = extOp.indexOf(valStr);
|
||||||
|
if (loc != -1) {
|
||||||
|
extOp = extOp.substring(0,loc) + extOp.substring(loc+valStr.length());
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
valStr = scalar.toString(10, false, true, "", "");
|
||||||
|
loc = extOp.indexOf(valStr);
|
||||||
|
if (loc != -1) {
|
||||||
|
extOp = extOp.substring(0,loc) + extOp.substring(loc+valStr.length());
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
valStr = scalar.toString(10, false, false, "", "");
|
||||||
|
loc = extOp.indexOf(valStr);
|
||||||
|
if (loc != -1) {
|
||||||
|
extOp = extOp.substring(0,loc) + extOp.substring(loc+valStr.length());
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
valStr = scalar.toString(16, false, false, "", "");
|
||||||
|
loc = extOp.indexOf(valStr);
|
||||||
|
if (loc != -1) {
|
||||||
|
extOp = extOp.substring(0,loc) + extOp.substring(loc+valStr.length());
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
valStr = scalar.toString(16, true, false, "", "");
|
||||||
|
loc = extOp.indexOf(valStr);
|
||||||
|
if (loc != -1) {
|
||||||
|
extOp = extOp.substring(0,loc) + extOp.substring(loc+valStr.length());
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
markBad(addr,"Missing Scalar", valStr);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (object instanceof Register) {
|
||||||
|
Register reg = (Register) object;
|
||||||
|
loc = extOp.indexOf(reg.getName().toLowerCase());
|
||||||
|
if (loc != -1) {
|
||||||
|
// check for '-' first
|
||||||
|
if (extOp.charAt(0) == '-') {
|
||||||
|
extOp = extOp.substring(1);
|
||||||
|
loc = 0;
|
||||||
|
subRegList = false;
|
||||||
|
}
|
||||||
|
extOp = extOp.substring(0,loc) + extOp.substring(loc+reg.getName().length());
|
||||||
|
if (extOp.length() > 0 && extOp.charAt(0) == '-') {
|
||||||
|
subRegList = true;
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// check for equivalent register
|
||||||
|
String equivReg = regGetEquivalent(reg.getName());
|
||||||
|
if (equivReg != null) {
|
||||||
|
loc = extOp.indexOf(equivReg);
|
||||||
|
if (loc != -1) {
|
||||||
|
extOp = extOp.substring(0,loc) + extOp.substring(loc+equivReg.length());
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
loc = extOp.indexOf('-'); // could be a register list, assume we will find beginning and end register
|
||||||
|
if (loc != -1) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
markBad(addr,"Missing Register", reg.toString());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (object instanceof Address) {
|
||||||
|
Address dest = (Address) object;
|
||||||
|
valStr = dest.toString(false,true);
|
||||||
|
valStr = "0x" + valStr;
|
||||||
|
loc = extOp.indexOf(valStr);
|
||||||
|
if (loc != -1) {
|
||||||
|
extOp = extOp.substring(0,loc) + extOp.substring(loc+valStr.length());
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
valStr = dest.toString(false,false);
|
||||||
|
valStr = "0x" + valStr;
|
||||||
|
loc = extOp.indexOf(valStr);
|
||||||
|
if (loc != -1) {
|
||||||
|
extOp = extOp.substring(0,loc) + extOp.substring(loc+valStr.length());
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
valStr = dest.toString(false,true);
|
||||||
|
loc = extOp.indexOf(valStr);
|
||||||
|
if (loc != -1) {
|
||||||
|
extOp = extOp.substring(0,loc) + extOp.substring(loc+valStr.length());
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
valStr = dest.toString(false,false);
|
||||||
|
loc = extOp.indexOf(valStr);
|
||||||
|
if (loc != -1) {
|
||||||
|
extOp = extOp.substring(0,loc) + extOp.substring(loc+valStr.length());
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
markBad(addr,"Missing Address", dest.toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
extOp = extOp.trim();
|
||||||
|
if (extOp.length() > 0 && !extOp.startsWith(";") && !extOp.startsWith("//") && !extOp.equals("#") && !extOp.matches("[0x]+")) {
|
||||||
|
markBad(addr,"Missing characters", extOp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
HashMap<String, String> equivRegisters = new HashMap<String, String>();
|
||||||
|
|
||||||
|
private String regGetEquivalent(String name) {
|
||||||
|
return equivRegisters.get(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void putEquivalent(String name, String equiv) {
|
||||||
|
equivRegisters.put(name, equiv);
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getExtOpStr(String str, int start, int sepEnd) {
|
||||||
|
String opS = null;
|
||||||
|
if (start == -1) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
if (sepEnd == -1) {
|
||||||
|
opS = str.substring(start);
|
||||||
|
} else {
|
||||||
|
opS = str.substring(start, sepEnd);
|
||||||
|
}
|
||||||
|
String extOp = opS.trim();
|
||||||
|
return extOp;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void markBad(Address addr, String type, String error) {
|
||||||
|
currentProgram.getBookmarkManager().setBookmark(addr, BookmarkType.WARNING,
|
||||||
|
type,
|
||||||
|
error);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void markErrorBad(Address addr, String type, String error) {
|
||||||
|
currentProgram.getBookmarkManager().setBookmark(addr, BookmarkType.ERROR,
|
||||||
|
Disassembler.ERROR_BOOKMARK_CATEGORY,
|
||||||
|
error);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void clearBad(Address addr) {
|
||||||
|
AddressSet set = new AddressSet(addr);
|
||||||
|
try {
|
||||||
|
currentProgram.getBookmarkManager().removeBookmarks(set, BookmarkType.WARNING, monitor);
|
||||||
|
currentProgram.getBookmarkManager().removeBookmarks(set, BookmarkType.ERROR, monitor);
|
||||||
|
} catch (CancelledException e) {
|
||||||
|
// do nothing
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,79 @@
|
|||||||
|
/* ###
|
||||||
|
* IP: GHIDRA
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
import ghidra.app.script.GhidraScript;
|
||||||
|
import ghidra.app.util.disassemble.GNUExternalDisassembler;
|
||||||
|
import ghidra.app.util.disassemble.GnuDisassembledInstruction;
|
||||||
|
import ghidra.program.model.address.Address;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class GNUDisassembleBlockScript extends GhidraScript {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void run() throws Exception {
|
||||||
|
|
||||||
|
if (currentProgram == null || currentAddress == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
GNUExternalDisassembler dis = new GNUExternalDisassembler();
|
||||||
|
|
||||||
|
Address addr = currentAddress.getNewAddress(currentAddress.getOffset() & -32); // block aligned address
|
||||||
|
|
||||||
|
List<GnuDisassembledInstruction> results = dis.getBlockDisassembly(currentProgram, addr, 5);
|
||||||
|
|
||||||
|
if (results == null) {
|
||||||
|
println("Block Disassembly Failed!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int maxByteLen = 0;
|
||||||
|
for (GnuDisassembledInstruction result : results) {
|
||||||
|
maxByteLen = Math.max(maxByteLen, result.getNumberOfBytesInInstruction());
|
||||||
|
}
|
||||||
|
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
for (GnuDisassembledInstruction result : results) {
|
||||||
|
sb.append(addr.toString());
|
||||||
|
sb.append(' ');
|
||||||
|
int cnt = 0;
|
||||||
|
byte[] bytes = new byte[result.getNumberOfBytesInInstruction()];
|
||||||
|
currentProgram.getMemory().getBytes(addr, bytes);
|
||||||
|
for (byte b : bytes) {
|
||||||
|
if (b >= 0 && b < 0x10) {
|
||||||
|
sb.append('0');
|
||||||
|
}
|
||||||
|
sb.append(Integer.toHexString(b & 0xff));
|
||||||
|
sb.append(' ');
|
||||||
|
++cnt;
|
||||||
|
}
|
||||||
|
if (cnt < maxByteLen) {
|
||||||
|
int pad = (maxByteLen - cnt) * 3;
|
||||||
|
for (int i = 0; i < pad; i++) {
|
||||||
|
sb.append(' ');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sb.append(result.getInstruction());
|
||||||
|
sb.append("\n");
|
||||||
|
addr = addr.add(bytes.length);
|
||||||
|
}
|
||||||
|
if (sb.length() != 0) {
|
||||||
|
println("Block Disassembly:\n" + sb.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,32 @@
|
|||||||
|
/* ###
|
||||||
|
* IP: GHIDRA
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package ghidra.app.util.disassemble;
|
||||||
|
|
||||||
|
import ghidra.program.model.lang.Language;
|
||||||
|
import ghidra.program.model.listing.CodeUnit;
|
||||||
|
import ghidra.util.classfinder.ExtensionPoint;
|
||||||
|
|
||||||
|
public interface ExternalDisassembler extends ExtensionPoint {
|
||||||
|
|
||||||
|
public String getDisassembly(CodeUnit cu) throws Exception;
|
||||||
|
|
||||||
|
public String getDisassemblyOfBytes(Language language, boolean isBigEndian, long address,
|
||||||
|
byte[] byteString) throws Exception;
|
||||||
|
|
||||||
|
public boolean isSupportedLanguage(Language language);
|
||||||
|
|
||||||
|
public void destroy();
|
||||||
|
}
|
@ -0,0 +1,166 @@
|
|||||||
|
/* ###
|
||||||
|
* IP: GHIDRA
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package ghidra.app.util.disassemble;
|
||||||
|
|
||||||
|
import java.awt.Color;
|
||||||
|
import java.math.BigInteger;
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
import docking.widgets.fieldpanel.field.*;
|
||||||
|
import docking.widgets.fieldpanel.support.FieldLocation;
|
||||||
|
import ghidra.app.util.HighlightProvider;
|
||||||
|
import ghidra.app.util.viewer.field.*;
|
||||||
|
import ghidra.app.util.viewer.format.FieldFormatModel;
|
||||||
|
import ghidra.app.util.viewer.proxy.ProxyObj;
|
||||||
|
import ghidra.framework.options.Options;
|
||||||
|
import ghidra.framework.options.ToolOptions;
|
||||||
|
import ghidra.program.model.lang.Language;
|
||||||
|
import ghidra.program.model.listing.CodeUnit;
|
||||||
|
import ghidra.program.util.ProgramLocation;
|
||||||
|
import ghidra.util.classfinder.ClassSearcher;
|
||||||
|
|
||||||
|
public class ExternalDisassemblyFieldFactory extends FieldFactory {
|
||||||
|
|
||||||
|
private static List<ExternalDisassembler> availableDisassemblers;
|
||||||
|
|
||||||
|
private static synchronized List<ExternalDisassembler> getAvailableDisassemblers() {
|
||||||
|
if (availableDisassemblers != null) {
|
||||||
|
return availableDisassemblers;
|
||||||
|
}
|
||||||
|
availableDisassemblers = new ArrayList<>();
|
||||||
|
|
||||||
|
// find the available external disassemblers
|
||||||
|
Set<ExternalDisassembler> extDisassemblers =
|
||||||
|
ClassSearcher.getInstances(ExternalDisassembler.class);
|
||||||
|
|
||||||
|
for (ExternalDisassembler disassember : extDisassemblers) {
|
||||||
|
availableDisassemblers.add(disassember);
|
||||||
|
}
|
||||||
|
return availableDisassemblers;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static final String FIELD_NAME = "External Disassembly";
|
||||||
|
|
||||||
|
public ExternalDisassemblyFieldFactory() {
|
||||||
|
super(FIELD_NAME);
|
||||||
|
}
|
||||||
|
|
||||||
|
private ExternalDisassemblyFieldFactory(FieldFormatModel model, HighlightProvider hlProvider,
|
||||||
|
Options displayOptions, Options fieldOptions) {
|
||||||
|
super(FIELD_NAME, model, hlProvider, displayOptions, fieldOptions);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void fieldOptionsChanged(Options options, String optionName, Object oldValue,
|
||||||
|
Object newValue) {
|
||||||
|
// have no options
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean acceptsType(int category, Class<?> proxyObjectClass) {
|
||||||
|
return (category == FieldFormatModel.INSTRUCTION_OR_DATA);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public FieldLocation getFieldLocation(ListingField bf, BigInteger index, int fieldNum,
|
||||||
|
ProgramLocation loc) {
|
||||||
|
if (loc instanceof ExternalDisassemblyFieldLocation) {
|
||||||
|
return new FieldLocation(index, fieldNum,
|
||||||
|
((ExternalDisassemblyFieldLocation) loc).getRow(),
|
||||||
|
((ExternalDisassemblyFieldLocation) loc).getCharOffset());
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ProgramLocation getProgramLocation(int row, int col, ListingField bf) {
|
||||||
|
ProxyObj<?> proxy = bf.getProxy();
|
||||||
|
Object obj = proxy.getObject();
|
||||||
|
if (!(obj instanceof CodeUnit)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
CodeUnit cu = (CodeUnit) obj;
|
||||||
|
|
||||||
|
return new ExternalDisassemblyFieldLocation(cu.getProgram(), cu.getMinAddress(), row, col);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public FieldFactory newInstance(FieldFormatModel formatModel,
|
||||||
|
HighlightProvider highlightProvider, ToolOptions options, ToolOptions fieldOptions) {
|
||||||
|
return new ExternalDisassemblyFieldFactory(formatModel, highlightProvider, options,
|
||||||
|
fieldOptions);
|
||||||
|
}
|
||||||
|
|
||||||
|
private ExternalDisassembler getDisassembler(Language language) {
|
||||||
|
for (ExternalDisassembler disassembler : getAvailableDisassemblers()) {
|
||||||
|
if (disassembler.isSupportedLanguage(language)) {
|
||||||
|
return disassembler;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ListingField getField(ProxyObj<?> proxy, int varWidth) {
|
||||||
|
if (!enabled) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
Object obj = proxy.getObject();
|
||||||
|
if (!(obj instanceof CodeUnit)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
CodeUnit cu = (CodeUnit) obj;
|
||||||
|
|
||||||
|
try {
|
||||||
|
String disassembly = getDisassemblyText(cu);
|
||||||
|
if (disassembly == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
AttributedString text = new AttributedString(disassembly, Color.black, getMetrics());
|
||||||
|
FieldElement fieldElement = new TextFieldElement(text, 0, 0);
|
||||||
|
return ListingTextField.createSingleLineTextField(this, proxy, fieldElement,
|
||||||
|
startX + varWidth, width, hlProvider);
|
||||||
|
}
|
||||||
|
catch (Exception e) {
|
||||||
|
return getErrorText(proxy, varWidth, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getDisassemblyText(CodeUnit cu) throws Exception {
|
||||||
|
Language language = cu.getProgram().getLanguage();
|
||||||
|
ExternalDisassembler disassembler = getDisassembler(language);
|
||||||
|
if (disassembler == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
String disassembly = disassembler.getDisassembly(cu);
|
||||||
|
if (disassembly == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return disassembly;
|
||||||
|
}
|
||||||
|
|
||||||
|
private ListingTextField getErrorText(ProxyObj<?> proxy, int varWidth, Exception e) {
|
||||||
|
String message = e.getMessage();
|
||||||
|
if (message == null) {
|
||||||
|
message = e.toString();
|
||||||
|
}
|
||||||
|
AttributedString errorText = new AttributedString(message, Color.red, getMetrics());
|
||||||
|
FieldElement fieldElement = new TextFieldElement(errorText, 0, 0);
|
||||||
|
return ListingTextField.createSingleLineTextField(this, proxy, fieldElement,
|
||||||
|
startX + varWidth, width, hlProvider);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,35 @@
|
|||||||
|
/* ###
|
||||||
|
* IP: GHIDRA
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package ghidra.app.util.disassemble;
|
||||||
|
|
||||||
|
import ghidra.program.model.address.Address;
|
||||||
|
import ghidra.program.model.listing.Program;
|
||||||
|
import ghidra.program.util.ProgramLocation;
|
||||||
|
|
||||||
|
public class ExternalDisassemblyFieldLocation extends ProgramLocation {
|
||||||
|
|
||||||
|
public ExternalDisassemblyFieldLocation(Program program, Address addr, int row, int charOffset) {
|
||||||
|
super(program, addr, row, 0, charOffset);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the row within a group of pcode strings.
|
||||||
|
*/
|
||||||
|
public ExternalDisassemblyFieldLocation() {
|
||||||
|
// for deserialization
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,43 @@
|
|||||||
|
/* ###
|
||||||
|
* IP: GHIDRA
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package ghidra.app.util.disassemble;
|
||||||
|
|
||||||
|
import ghidra.util.Msg;
|
||||||
|
|
||||||
|
import java.io.*;
|
||||||
|
|
||||||
|
public class ExternalStreamHandler extends Thread {
|
||||||
|
private InputStream inStream;
|
||||||
|
|
||||||
|
ExternalStreamHandler(InputStream inStream) {
|
||||||
|
this.inStream = inStream;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
try {
|
||||||
|
InputStreamReader inStreamReader = new InputStreamReader(inStream);
|
||||||
|
BufferedReader buffReader = new BufferedReader(inStreamReader);
|
||||||
|
String line;
|
||||||
|
while ((line = buffReader.readLine()) != null) {
|
||||||
|
Msg.error(ExternalDisassemblyFieldFactory.class, "Error in Disassembler: " + line);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (IOException ioe) {
|
||||||
|
ioe.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,646 @@
|
|||||||
|
/* ###
|
||||||
|
* IP: GHIDRA
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package ghidra.app.util.disassemble;
|
||||||
|
|
||||||
|
import java.io.*;
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
import generic.jar.ResourceFile;
|
||||||
|
import ghidra.app.util.bin.ByteProvider;
|
||||||
|
import ghidra.app.util.bin.MemoryByteProvider;
|
||||||
|
import ghidra.framework.*;
|
||||||
|
import ghidra.program.model.address.Address;
|
||||||
|
import ghidra.program.model.address.AddressOutOfBoundsException;
|
||||||
|
import ghidra.program.model.lang.Language;
|
||||||
|
import ghidra.program.model.lang.LanguageID;
|
||||||
|
import ghidra.program.model.listing.CodeUnit;
|
||||||
|
import ghidra.program.model.listing.Program;
|
||||||
|
import ghidra.program.model.mem.MemBuffer;
|
||||||
|
import ghidra.program.model.mem.MemoryAccessException;
|
||||||
|
import ghidra.util.Msg;
|
||||||
|
|
||||||
|
public class GNUExternalDisassembler implements ExternalDisassembler {
|
||||||
|
|
||||||
|
private static final String UNSUPPORTED = "UNSUPPORTED";
|
||||||
|
|
||||||
|
// magic values for gdis that direct it to read bytes from stdin
|
||||||
|
private static final String READ_FROM_STDIN_PARAMETER = "stdin";
|
||||||
|
private static final String SEPARATOR_CHARACTER = "\n";
|
||||||
|
private static final String ADDRESS_OUT_OF_BOUNDS = "is out of bounds.";
|
||||||
|
private static final String ENDING_STRING = "EOF";
|
||||||
|
private static final int NUM_BYTES = 32;
|
||||||
|
|
||||||
|
private static final String MAP_FILENAME = "LanguageMap.txt";
|
||||||
|
private static final String GNU_DISASSEMBLER_MODULE_NAME = "GnuDisassembler";
|
||||||
|
private static final String GDIS_EXE =
|
||||||
|
Platform.CURRENT_PLATFORM.getOperatingSystem() == OperatingSystem.WINDOWS ? "gdis.exe"
|
||||||
|
: "gdis";
|
||||||
|
|
||||||
|
private static HashMap<String, File> languageGdisMap;
|
||||||
|
private static File defaultGdisExecFile;
|
||||||
|
private static File gdisDataDirectory;
|
||||||
|
|
||||||
|
private static Map<LanguageID, GdisConfig> configCache = new HashMap<>();
|
||||||
|
private static boolean missingExtensionReported;
|
||||||
|
|
||||||
|
private GdisConfig currentConfig;
|
||||||
|
private boolean hadFailure;
|
||||||
|
|
||||||
|
private Process disassemblerProcess;
|
||||||
|
private BufferedReader buffReader;
|
||||||
|
private OutputStreamWriter outputWriter;
|
||||||
|
|
||||||
|
// private LanguageID lastLanguageWarnedAbout;
|
||||||
|
|
||||||
|
public GNUExternalDisassembler() throws Exception {
|
||||||
|
initialize();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void destroy() {
|
||||||
|
if (disassemblerProcess != null) {
|
||||||
|
disassemblerProcess.destroy();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isSupportedLanguage(Language language) {
|
||||||
|
GdisConfig gdisConfig = checkLanguage(language);
|
||||||
|
return gdisConfig != null && gdisConfig.architecture != UNSUPPORTED;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void reportMultipleMappings(Language language) {
|
||||||
|
List<String> externalNames = language.getLanguageDescription().getExternalNames("gnu");
|
||||||
|
if (externalNames != null && externalNames.size() > 1) {
|
||||||
|
LanguageID currentLanguageID = language.getLanguageID();
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
boolean prependSeparator = false;
|
||||||
|
for (String name : externalNames) {
|
||||||
|
if (prependSeparator)
|
||||||
|
sb.append(", ");
|
||||||
|
sb.append(name);
|
||||||
|
prependSeparator = true;
|
||||||
|
}
|
||||||
|
Msg.warn(GNUExternalDisassembler.class,
|
||||||
|
"Language " + currentLanguageID + " illegally maps to multiple (" +
|
||||||
|
externalNames.size() + ") external gnu names: " + sb.toString() +
|
||||||
|
". The first external name will be used.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class GdisConfig {
|
||||||
|
|
||||||
|
String languageId;
|
||||||
|
boolean isBigEndian;
|
||||||
|
|
||||||
|
String architecture;
|
||||||
|
String machineId;
|
||||||
|
File gdisExecFile;
|
||||||
|
boolean usingDefault;
|
||||||
|
|
||||||
|
GdisConfig(Language language, boolean isBigEndian) {
|
||||||
|
|
||||||
|
this.languageId = language.getLanguageID().toString();
|
||||||
|
this.isBigEndian = isBigEndian;
|
||||||
|
|
||||||
|
List<String> architectures = language.getLanguageDescription().getExternalNames("gnu");
|
||||||
|
//get first non-null
|
||||||
|
if (architectures != null && architectures.size() > 0) {
|
||||||
|
architecture = architectures.get(0);
|
||||||
|
if (architectures.size() > 1) {
|
||||||
|
reportMultipleMappings(language);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (architecture == null) {
|
||||||
|
architecture = UNSUPPORTED;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
machineId = "0x0";
|
||||||
|
|
||||||
|
// handle numeric entry which combines architecture and machineId
|
||||||
|
if (architecture.startsWith("0x")) {
|
||||||
|
String[] parts = architecture.split(":");
|
||||||
|
architecture = parts[0];
|
||||||
|
machineId = parts[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
gdisExecFile = languageGdisMap.get(languageId);
|
||||||
|
if (gdisExecFile == null) {
|
||||||
|
gdisExecFile = defaultGdisExecFile;
|
||||||
|
usingDefault = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
GdisConfig(Language lang) {
|
||||||
|
this(lang, lang.isBigEndian());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object obj) {
|
||||||
|
if (!(obj instanceof GdisConfig)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// assume config will match for a given language
|
||||||
|
return languageId.equals(((GdisConfig) obj).languageId);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return languageId.hashCode();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static synchronized GdisConfig checkLanguage(Language lang) {
|
||||||
|
LanguageID languageId = lang.getLanguageID();
|
||||||
|
if (configCache.containsKey(languageId)) {
|
||||||
|
return configCache.get(languageId);
|
||||||
|
}
|
||||||
|
GdisConfig config = new GdisConfig(lang);
|
||||||
|
if (config.architecture == UNSUPPORTED) {
|
||||||
|
Msg.warn(GNUExternalDisassembler.class,
|
||||||
|
"Language not supported (ldefs 'gnu' map entry not found): " + languageId);
|
||||||
|
}
|
||||||
|
else if (gdisDataDirectory == null) {
|
||||||
|
config = null;
|
||||||
|
if (!missingExtensionReported) {
|
||||||
|
missingExtensionReported = true;
|
||||||
|
Msg.showError(GNUExternalDisassembler.class, null, "GNU Disassembler Not Found",
|
||||||
|
"External Disassembler extension module not installed: " +
|
||||||
|
GNU_DISASSEMBLER_MODULE_NAME);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (config.gdisExecFile == null) {
|
||||||
|
boolean usingDefault = config.usingDefault;
|
||||||
|
config = null;
|
||||||
|
if (usingDefault) {
|
||||||
|
if (!missingExtensionReported) {
|
||||||
|
missingExtensionReported = true;
|
||||||
|
Msg.showError(GNUExternalDisassembler.class, null, "GNU Disassembler Not Found",
|
||||||
|
"External GNU Disassembler not found (requires install and build of " +
|
||||||
|
GNU_DISASSEMBLER_MODULE_NAME + " extension): " + GDIS_EXE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
Msg.showError(GNUExternalDisassembler.class, null, "GNU Disassembler Not Found",
|
||||||
|
"External GNU Disassembler not found for language (" + lang.getLanguageID() +
|
||||||
|
", see LanguageMap.txt)");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
configCache.put(languageId, config);
|
||||||
|
return config;
|
||||||
|
}
|
||||||
|
|
||||||
|
private int pow2(int pow) {
|
||||||
|
int r = 1;
|
||||||
|
for (int i = 1; i <= pow; i++) {
|
||||||
|
r *= 2;
|
||||||
|
}
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get detailed instruction list for a block of instructions.
|
||||||
|
*
|
||||||
|
* @param lang
|
||||||
|
* processor language (corresponding LanguageID must be defined
|
||||||
|
* within LanguageMap.txt)
|
||||||
|
* @param blockAddr
|
||||||
|
* start of block ( must be true: (offset & -(2^blockSizeFactor)
|
||||||
|
* == offset)
|
||||||
|
* @param blockSizeFactor
|
||||||
|
* the block size factor where blockSize = 2^blockSizeFactor
|
||||||
|
* (must be > 0)
|
||||||
|
* @param byteProvider
|
||||||
|
* provider for block of bytes to be disassembled starting at
|
||||||
|
* offset 0
|
||||||
|
* @return list of instructions or null if language not supported by GNU
|
||||||
|
* Disassembler
|
||||||
|
* @throws Exception
|
||||||
|
*/
|
||||||
|
public List<GnuDisassembledInstruction> getBlockDisassembly(Language lang, Address blockAddr,
|
||||||
|
int blockSizeFactor, ByteProvider byteProvider) throws Exception {
|
||||||
|
|
||||||
|
GdisConfig gdisConfig = checkLanguage(lang);
|
||||||
|
if (gdisConfig == null || gdisConfig.architecture == UNSUPPORTED) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (blockSizeFactor < 0 || blockSizeFactor > 8) {
|
||||||
|
throw new IllegalArgumentException("blockSizeFactor must be > 0 and <= 8");
|
||||||
|
}
|
||||||
|
int blockSize = pow2(blockSizeFactor);
|
||||||
|
|
||||||
|
if ((blockAddr.getOffset() & -blockSize) != blockAddr.getOffset()) {
|
||||||
|
throw new IllegalArgumentException("Address must be block aligned");
|
||||||
|
}
|
||||||
|
|
||||||
|
long addressOffset = blockAddr.getAddressableWordOffset();
|
||||||
|
String address = "0x" + Long.toHexString(addressOffset);
|
||||||
|
|
||||||
|
// for aligned languages, don't try on non-aligned block addr/size.
|
||||||
|
int alignment = lang.getInstructionAlignment();
|
||||||
|
if (blockAddr.getOffset() % alignment != 0) {
|
||||||
|
throw new IllegalArgumentException(
|
||||||
|
"Address does not satisfy instruction alignment constraint: " + alignment);
|
||||||
|
}
|
||||||
|
|
||||||
|
String bytes = getBytes(byteProvider, blockSize);
|
||||||
|
|
||||||
|
return runDisassembler(gdisConfig, address, bytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<GnuDisassembledInstruction> getBlockDisassembly(Program program, Address addr,
|
||||||
|
int blockSizeFactor) throws Exception {
|
||||||
|
|
||||||
|
if (blockSizeFactor < 0 || blockSizeFactor > 8) {
|
||||||
|
throw new IllegalArgumentException("blockSizeFactor must be > 0 and <= 8");
|
||||||
|
}
|
||||||
|
int blockSize = pow2(blockSizeFactor);
|
||||||
|
|
||||||
|
Address blockAddr = addr.getNewAddress(addr.getOffset() & -blockSize); // block
|
||||||
|
// aligned
|
||||||
|
// address
|
||||||
|
|
||||||
|
return getBlockDisassembly(program.getLanguage(), blockAddr, blockSizeFactor,
|
||||||
|
new MemoryByteProvider(program.getMemory(), blockAddr));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getDisassembly(CodeUnit cu) throws Exception {
|
||||||
|
|
||||||
|
GdisConfig gdisConfig = checkLanguage(cu.getProgram().getLanguage());
|
||||||
|
if (gdisConfig == null || gdisConfig.architecture == UNSUPPORTED) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
long addressOffset = cu.getAddress().getAddressableWordOffset();
|
||||||
|
String address = "0x" + Long.toHexString(addressOffset);
|
||||||
|
|
||||||
|
// for aligned languages, don't try on non-aligned locations.
|
||||||
|
if (cu.getMinAddress().getOffset() %
|
||||||
|
cu.getProgram().getLanguage().getInstructionAlignment() != 0) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
String bytes = getBytes(cu, NUM_BYTES);
|
||||||
|
if (bytes == null) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
List<GnuDisassembledInstruction> disassembly = runDisassembler(gdisConfig, address, bytes);
|
||||||
|
|
||||||
|
if (disassembly == null || disassembly.size() == 0 || disassembly.get(0) == null) {
|
||||||
|
return "(bad)";
|
||||||
|
}
|
||||||
|
return disassembly.get(0).toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
// disassembler without having to have a code unit
|
||||||
|
@Override
|
||||||
|
public String getDisassemblyOfBytes(Language language, boolean isBigEndian, long addressOffset,
|
||||||
|
byte[] bytes) throws Exception {
|
||||||
|
|
||||||
|
GdisConfig gdisConfig = new GdisConfig(language, isBigEndian);
|
||||||
|
if (gdisConfig.architecture == UNSUPPORTED || gdisConfig.gdisExecFile == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
String bytesString = converBytesToString(bytes);
|
||||||
|
|
||||||
|
String address = "0x" + Long.toHexString(addressOffset);
|
||||||
|
|
||||||
|
List<GnuDisassembledInstruction> disassembly =
|
||||||
|
runDisassembler(gdisConfig, address, bytesString);
|
||||||
|
|
||||||
|
if (disassembly == null || disassembly.isEmpty() || disassembly.get(0) == null) {
|
||||||
|
return "(bad)";
|
||||||
|
}
|
||||||
|
return disassembly.get(0).toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
private String converBytesToString(byte[] bytes) {
|
||||||
|
String byteString = null;
|
||||||
|
for (byte thisByte : bytes) {
|
||||||
|
String thisByteString = Integer.toHexString(thisByte);
|
||||||
|
if (thisByteString.length() == 1)
|
||||||
|
thisByteString = "0" + thisByteString; // pad single digits
|
||||||
|
if (thisByteString.length() > 2)
|
||||||
|
thisByteString = thisByteString.substring(thisByteString.length() - 2);
|
||||||
|
// append this byte's hex string to the larger word length string
|
||||||
|
byteString = byteString + thisByteString;
|
||||||
|
}
|
||||||
|
|
||||||
|
return byteString;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean setupDisassembler(GdisConfig gdisConfig) {
|
||||||
|
|
||||||
|
if (disassemblerProcess != null) {
|
||||||
|
disassemblerProcess.destroy();
|
||||||
|
disassemblerProcess = null;
|
||||||
|
outputWriter = null;
|
||||||
|
buffReader = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.currentConfig = gdisConfig;
|
||||||
|
hadFailure = false;
|
||||||
|
|
||||||
|
String endianString = gdisConfig.isBigEndian ? "0x00" : "0x01"; // 0x0 is big, 0x1 is little endian
|
||||||
|
|
||||||
|
// NOTE: valid target must be specified but has no effect on results
|
||||||
|
String cmds[] = { gdisConfig.gdisExecFile.getAbsolutePath(), "pef", gdisConfig.architecture,
|
||||||
|
gdisConfig.machineId, endianString, "0x0",
|
||||||
|
gdisDataDirectory.getAbsolutePath() + File.separator,
|
||||||
|
GNUExternalDisassembler.READ_FROM_STDIN_PARAMETER };
|
||||||
|
|
||||||
|
StringBuilder buf = new StringBuilder();
|
||||||
|
for (String str : cmds) {
|
||||||
|
boolean addQuotes = str.indexOf(' ') >= 0;
|
||||||
|
if (addQuotes) {
|
||||||
|
buf.append('\"');
|
||||||
|
}
|
||||||
|
buf.append(str);
|
||||||
|
if (addQuotes) {
|
||||||
|
buf.append('\"');
|
||||||
|
}
|
||||||
|
buf.append(' ');
|
||||||
|
}
|
||||||
|
Msg.debug(this, "Starting gdis: " + buf.toString());
|
||||||
|
|
||||||
|
try {
|
||||||
|
Runtime rt = Runtime.getRuntime();
|
||||||
|
disassemblerProcess = rt.exec(cmds, null, gdisConfig.gdisExecFile.getParentFile());
|
||||||
|
}
|
||||||
|
catch (IOException e) {
|
||||||
|
buf = new StringBuilder();
|
||||||
|
for (String arg : cmds) {
|
||||||
|
buf.append("\"");
|
||||||
|
buf.append(arg);
|
||||||
|
buf.append("\" ");
|
||||||
|
}
|
||||||
|
Msg.debug(this, "GNU Disassembly setup failed, exec command: " + buf);
|
||||||
|
Msg.showError(this, null, "GNU Disassembler Error",
|
||||||
|
"Disassembler setup execution error: " + e.getMessage(), e);
|
||||||
|
hadFailure = true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<GnuDisassembledInstruction> runDisassembler(GdisConfig gdisConfig,
|
||||||
|
String addrString, String bytes) throws IOException {
|
||||||
|
|
||||||
|
// if this is the first time running the disassembler process, or a
|
||||||
|
// parameter has changed (notably, not the address--we pass that in
|
||||||
|
// every time)
|
||||||
|
boolean sameConfig = gdisConfig.equals(currentConfig);
|
||||||
|
if (sameConfig && hadFailure) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (disassemblerProcess == null || !sameConfig) {
|
||||||
|
|
||||||
|
if (!setupDisassembler(gdisConfig))
|
||||||
|
return null;
|
||||||
|
|
||||||
|
outputWriter = new OutputStreamWriter(disassemblerProcess.getOutputStream());
|
||||||
|
|
||||||
|
InputStreamReader inStreamReader =
|
||||||
|
new InputStreamReader(disassemblerProcess.getInputStream());
|
||||||
|
buffReader = new BufferedReader(inStreamReader);
|
||||||
|
|
||||||
|
ExternalStreamHandler errorHandler =
|
||||||
|
new ExternalStreamHandler(disassemblerProcess.getErrorStream());
|
||||||
|
errorHandler.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!disassemblerProcess.isAlive()) {
|
||||||
|
return null; // if process previously died return nothing - quickly
|
||||||
|
}
|
||||||
|
|
||||||
|
String disassemblyRequest = addrString + SEPARATOR_CHARACTER + bytes + SEPARATOR_CHARACTER;
|
||||||
|
try {
|
||||||
|
outputWriter.write(disassemblyRequest);
|
||||||
|
outputWriter.flush();
|
||||||
|
return getDisassembledInstruction();
|
||||||
|
}
|
||||||
|
catch (IOException e) {
|
||||||
|
// force a restart of the disassembler on next call to this function
|
||||||
|
// TODO: Should we not do this to avoid repeated failure and severe slowdown?
|
||||||
|
// User must exit or switch configs/programs to retry after failure
|
||||||
|
//disassemblerProcess.destroy();
|
||||||
|
//disassemblerProcess = null; // assumes process exit
|
||||||
|
Msg.error(this, "Last gdis request failed: " + disassemblyRequest);
|
||||||
|
throw new IOException("gdis execution error", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<GnuDisassembledInstruction> getDisassembledInstruction() throws IOException {
|
||||||
|
|
||||||
|
List<GnuDisassembledInstruction> results = new ArrayList<>();
|
||||||
|
String instructionLine;
|
||||||
|
|
||||||
|
boolean error = false;
|
||||||
|
do {
|
||||||
|
instructionLine = buffReader.readLine();
|
||||||
|
if (!error && instructionLine != null && !instructionLine.equals(ENDING_STRING) &&
|
||||||
|
(instructionLine.indexOf(ADDRESS_OUT_OF_BOUNDS) < 0) &&
|
||||||
|
!instructionLine.startsWith("Usage:") && !instructionLine.startsWith("Debug:")) {
|
||||||
|
|
||||||
|
String instructionMetadataLine = buffReader.readLine();
|
||||||
|
if (!instructionMetadataLine.startsWith("Info: ")) {
|
||||||
|
// TODO, throw an "ExternalDisassemblerInterfaceException"
|
||||||
|
// or some such
|
||||||
|
error = true; // still need to consume remainder of input
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
String[] metadata = instructionMetadataLine.substring("Info: ".length()).split(",");
|
||||||
|
results.add(new GnuDisassembledInstruction(instructionLine.replace('\t', ' '),
|
||||||
|
Integer.parseInt(metadata[0]), "1".equals(metadata[1]),
|
||||||
|
Integer.parseInt(metadata[2]), Integer.parseInt(metadata[3]),
|
||||||
|
Integer.parseInt(metadata[4])));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
while (instructionLine != null && !instructionLine.equals(ENDING_STRING));
|
||||||
|
|
||||||
|
if (!disassemblerProcess.isAlive()) {
|
||||||
|
throw new IOException("GNU disassembler process died unexpectedly.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (error) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return results;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getBytes(ByteProvider byteProvider, int size) throws IOException {
|
||||||
|
StringBuffer byteString = new StringBuffer();
|
||||||
|
for (int i = 0; i < size; i++) {
|
||||||
|
byteString.append(formatHexString(byteProvider.readByte(i)));
|
||||||
|
}
|
||||||
|
return byteString.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getBytes(MemBuffer mem, int size) {
|
||||||
|
StringBuffer byteString = new StringBuffer();
|
||||||
|
for (int i = 0; i < size; i++) {
|
||||||
|
try {
|
||||||
|
byteString.append(formatHexString(mem.getByte(i)));
|
||||||
|
}
|
||||||
|
catch (AddressOutOfBoundsException e) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
catch (MemoryAccessException e) {
|
||||||
|
if (i > 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return byteString.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
private String formatHexString(byte byteToFix) {
|
||||||
|
String byteString = "";
|
||||||
|
String singleByte = "";
|
||||||
|
if (byteToFix < 0) {
|
||||||
|
singleByte = Integer.toHexString(byteToFix + 256);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
singleByte = Integer.toHexString(byteToFix);
|
||||||
|
}
|
||||||
|
if (singleByte.length() == 1) {
|
||||||
|
byteString += '0';
|
||||||
|
}
|
||||||
|
byteString += singleByte;
|
||||||
|
return byteString;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static synchronized void initialize() throws Exception {
|
||||||
|
if (languageGdisMap != null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
languageGdisMap = new HashMap<>();
|
||||||
|
|
||||||
|
try {
|
||||||
|
// sample elf files located in data directory
|
||||||
|
ResourceFile dataDir =
|
||||||
|
Application.getModuleSubDirectory(GNU_DISASSEMBLER_MODULE_NAME, "data");
|
||||||
|
gdisDataDirectory = dataDir.getFile(false);
|
||||||
|
defaultGdisExecFile = Application.getOSFile(GNU_DISASSEMBLER_MODULE_NAME, GDIS_EXE);
|
||||||
|
}
|
||||||
|
catch (FileNotFoundException e) {
|
||||||
|
// ignore
|
||||||
|
}
|
||||||
|
|
||||||
|
if (gdisDataDirectory == null) {
|
||||||
|
Msg.warn(GNUExternalDisassembler.class,
|
||||||
|
"Use of External GNU Disassembler requires installation of extension: " +
|
||||||
|
GNU_DISASSEMBLER_MODULE_NAME);
|
||||||
|
}
|
||||||
|
|
||||||
|
initializeMaps();
|
||||||
|
|
||||||
|
if (defaultGdisExecFile == null || !defaultGdisExecFile.canExecute()) {
|
||||||
|
Msg.warn(GNUExternalDisassembler.class,
|
||||||
|
"External GNU Disassembler not found: " + GDIS_EXE);
|
||||||
|
defaultGdisExecFile = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Process all language maps defined by any module. Any alternate external disassembler
|
||||||
|
* executable will be looked for within the os directory of the contributing module or
|
||||||
|
* within the gdis module
|
||||||
|
* @throws Exception
|
||||||
|
*/
|
||||||
|
private static void initializeMaps() {
|
||||||
|
for (ResourceFile file : Application.findFilesByExtensionInApplication(".txt")) {
|
||||||
|
if (MAP_FILENAME.equals(file.getName())) {
|
||||||
|
initializeMap(file);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void initializeMap(ResourceFile mapFile) {
|
||||||
|
|
||||||
|
ResourceFile moduleForResourceFile = Application.getModuleContainingResourceFile(mapFile);
|
||||||
|
if (moduleForResourceFile == null) {
|
||||||
|
Msg.error(GNUExternalDisassembler.class,
|
||||||
|
"Failed to identify module containing file: " + mapFile);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Reader mapFileReader = null;
|
||||||
|
try {
|
||||||
|
mapFileReader = new InputStreamReader(mapFile.getInputStream());
|
||||||
|
BufferedReader reader = new BufferedReader(mapFileReader);
|
||||||
|
String line = null;
|
||||||
|
while ((line = reader.readLine()) != null) {
|
||||||
|
if (line.startsWith("//") || line.isEmpty()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
String[] parts = line.split("#");
|
||||||
|
if (parts.length > 1) {
|
||||||
|
|
||||||
|
//System.out.println("found: " + parts[0] + " . " + parts[1]);
|
||||||
|
|
||||||
|
// TODO: should probably store exe module/name in map and defer search
|
||||||
|
// until GdisConfig is created. This will allow us to complain about a
|
||||||
|
// missing exe when it is needed/used.
|
||||||
|
|
||||||
|
String gdisExe = parts[1];
|
||||||
|
if (Platform.CURRENT_PLATFORM.getOperatingSystem() == OperatingSystem.WINDOWS) {
|
||||||
|
gdisExe = gdisExe + ".exe";
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
File customGdisExecFile;
|
||||||
|
try {
|
||||||
|
customGdisExecFile =
|
||||||
|
Application.getOSFile(moduleForResourceFile.getName(), gdisExe);
|
||||||
|
}
|
||||||
|
catch (FileNotFoundException e) {
|
||||||
|
customGdisExecFile = Application.getOSFile(gdisExe);
|
||||||
|
}
|
||||||
|
languageGdisMap.put(parts[0], customGdisExecFile);
|
||||||
|
}
|
||||||
|
catch (FileNotFoundException e) {
|
||||||
|
Msg.error(GNUExternalDisassembler.class,
|
||||||
|
"External disassembler not found (" + parts[0] + "): " + gdisExe);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception e) {
|
||||||
|
Msg.error(GNUExternalDisassembler.class,
|
||||||
|
"Error reading from language mapping file: " + mapFile, e);
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
if (mapFileReader != null) {
|
||||||
|
try {
|
||||||
|
mapFileReader.close();
|
||||||
|
}
|
||||||
|
catch (Exception e) {
|
||||||
|
// we tried
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,82 @@
|
|||||||
|
/* ###
|
||||||
|
* IP: GHIDRA
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package ghidra.app.util.disassemble;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Holds the disassembled string of an instruction and the extra information
|
||||||
|
* (type, number of bytes disassembled to produce instruction, etc.) of bytes
|
||||||
|
* disassembled by the GNU disassembler.
|
||||||
|
*/
|
||||||
|
public class GnuDisassembledInstruction {
|
||||||
|
|
||||||
|
private final String instruction;
|
||||||
|
private final int bytesInInstruction;
|
||||||
|
|
||||||
|
private final int branchDelayInstructions;
|
||||||
|
private final int dataSize;
|
||||||
|
private final DIS_INSN_TYPE instructionType;
|
||||||
|
private final boolean isValid;
|
||||||
|
|
||||||
|
// from GNU binutils include/dis-asm.h
|
||||||
|
enum DIS_INSN_TYPE {
|
||||||
|
dis_noninsn, /* Not a valid instruction. */
|
||||||
|
dis_nonbranch, /* Not a branch instruction. */
|
||||||
|
dis_branch, /* Unconditional branch. */
|
||||||
|
dis_condbranch, /* Conditional branch. */
|
||||||
|
dis_jsr, /* Jump to subroutine. */
|
||||||
|
dis_condjsr, /* Conditional jump to subroutine. */
|
||||||
|
dis_dref, /* Data reference instruction. */
|
||||||
|
dis_dref2 /* Two data references in instruction. */
|
||||||
|
}
|
||||||
|
|
||||||
|
public GnuDisassembledInstruction(String instructionLine, int bytesInInstruction,
|
||||||
|
boolean isValid, int branchDelayInstructions, int dataSize, int disInsnTypeOrdinal) {
|
||||||
|
|
||||||
|
this.instruction = instructionLine.trim();
|
||||||
|
this.bytesInInstruction = bytesInInstruction;
|
||||||
|
|
||||||
|
this.isValid = isValid;
|
||||||
|
this.branchDelayInstructions = branchDelayInstructions;
|
||||||
|
this.dataSize = dataSize;
|
||||||
|
this.instructionType = DIS_INSN_TYPE.values()[disInsnTypeOrdinal];
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getNumberOfBytesInInstruction() {
|
||||||
|
return bytesInInstruction;
|
||||||
|
}
|
||||||
|
|
||||||
|
public DIS_INSN_TYPE getInstructionType() {
|
||||||
|
return isValid ? instructionType : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getBranchDelayInstructions() {
|
||||||
|
return isValid ? branchDelayInstructions : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getDataSize() {
|
||||||
|
return isValid ? dataSize : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getInstruction() {
|
||||||
|
return instruction;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return instruction;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -142,20 +142,22 @@ public class ClassSearcher {
|
|||||||
}
|
}
|
||||||
catch (InstantiationException e) {
|
catch (InstantiationException e) {
|
||||||
Msg.showError(ClassSearcher.class, null, "Error Instantiating Extension Point",
|
Msg.showError(ClassSearcher.class, null, "Error Instantiating Extension Point",
|
||||||
"Error creating class " + clazz.getName() + " for extension " + c.getName() +
|
"Error creating class " + clazz.getSimpleName() + " for extension " +
|
||||||
|
c.getName() +
|
||||||
". Discovered class is not a concrete implementation or does not " +
|
". Discovered class is not a concrete implementation or does not " +
|
||||||
"have a nullary constructor!",
|
"have a nullary constructor!",
|
||||||
e);
|
e);
|
||||||
}
|
}
|
||||||
catch (IllegalAccessException e) {
|
catch (IllegalAccessException e) {
|
||||||
Msg.showError(ClassSearcher.class, null, "Error Instantiating Extension Point",
|
Msg.showError(ClassSearcher.class, null, "Error Instantiating Extension Point",
|
||||||
"Error creating class " + clazz.getName() + " for extension " + c.getName() +
|
"Error creating class " + clazz.getSimpleName() + " for extension " +
|
||||||
|
c.getName() +
|
||||||
". Discovered class does not have a public, default constructor!",
|
". Discovered class does not have a public, default constructor!",
|
||||||
e);
|
e);
|
||||||
}
|
}
|
||||||
catch (SecurityException e) {
|
catch (SecurityException e) {
|
||||||
String message = "Error creating class " + clazz.getName() + " for extension " +
|
String message = "Error creating class " + clazz.getSimpleName() +
|
||||||
c.getName() + ". Security Exception!";
|
" for extension " + c.getName() + ". Security Exception!";
|
||||||
Msg.showError(ClassSearcher.class, null, "Error Instantiating Extension Point",
|
Msg.showError(ClassSearcher.class, null, "Error Instantiating Extension Point",
|
||||||
message, e);
|
message, e);
|
||||||
|
|
||||||
@ -163,7 +165,7 @@ public class ClassSearcher {
|
|||||||
}
|
}
|
||||||
catch (Exception e) {
|
catch (Exception e) {
|
||||||
Msg.showError(ClassSearcher.class, null, "Error Creating Extension Point",
|
Msg.showError(ClassSearcher.class, null, "Error Creating Extension Point",
|
||||||
"Error creating class " + clazz.getName() +
|
"Error creating class " + clazz.getSimpleName() +
|
||||||
" when creating extension points for " + c.getName(),
|
" when creating extension points for " + c.getName(),
|
||||||
e);
|
e);
|
||||||
}
|
}
|
||||||
|
@ -36,6 +36,7 @@ class ExtensionDetailsPanel extends AbstractDetailsPanel {
|
|||||||
private static SimpleAttributeSet authorAttrSet;
|
private static SimpleAttributeSet authorAttrSet;
|
||||||
private static SimpleAttributeSet createdOnAttrSet;
|
private static SimpleAttributeSet createdOnAttrSet;
|
||||||
private static SimpleAttributeSet versionAttrSet;
|
private static SimpleAttributeSet versionAttrSet;
|
||||||
|
private static SimpleAttributeSet pathAttrSet;
|
||||||
|
|
||||||
ExtensionDetailsPanel(ExtensionTablePanel tablePanel) {
|
ExtensionDetailsPanel(ExtensionTablePanel tablePanel) {
|
||||||
super();
|
super();
|
||||||
@ -105,6 +106,12 @@ class ExtensionDetailsPanel extends AbstractDetailsPanel {
|
|||||||
insertRowValue(buffer, version, versionAttrSet);
|
insertRowValue(buffer, version, versionAttrSet);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
String installPath = details.getInstallPath();
|
||||||
|
if (installPath != null) {
|
||||||
|
insertRowTitle(buffer, "Install Path");
|
||||||
|
insertRowValue(buffer, installPath, pathAttrSet);
|
||||||
|
}
|
||||||
|
|
||||||
buffer.append("</TABLE>");
|
buffer.append("</TABLE>");
|
||||||
|
|
||||||
textLabel.setText(buffer.toString());
|
textLabel.setText(buffer.toString());
|
||||||
@ -119,5 +126,6 @@ class ExtensionDetailsPanel extends AbstractDetailsPanel {
|
|||||||
authorAttrSet = createAttributeSet(Color.BLUE);
|
authorAttrSet = createAttributeSet(Color.BLUE);
|
||||||
createdOnAttrSet = createAttributeSet(Color.BLUE);
|
createdOnAttrSet = createAttributeSet(Color.BLUE);
|
||||||
versionAttrSet = createAttributeSet(Color.BLUE);
|
versionAttrSet = createAttributeSet(Color.BLUE);
|
||||||
|
pathAttrSet = createAttributeSet(Color.BLUE);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
##VERSION: 2.0
|
##VERSION: 2.0
|
||||||
README.txt||GHIDRA||||END|
|
README.txt||GHIDRA||||END|
|
||||||
|
distributableGPLExtension.gradle||GHIDRA||||END|
|
||||||
distributableGPLModule.gradle||GHIDRA||||END|
|
distributableGPLModule.gradle||GHIDRA||||END|
|
||||||
distributableGhidraExtension.gradle||GHIDRA||||END|
|
distributableGhidraExtension.gradle||GHIDRA||||END|
|
||||||
distributableGhidraModule.gradle||GHIDRA||||END|
|
distributableGhidraModule.gradle||GHIDRA||||END|
|
||||||
|
26
gradle/distributableGPLExtension.gradle
Normal file
26
gradle/distributableGPLExtension.gradle
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
/*****************************************************************************************
|
||||||
|
This file is a "mix-in" gradle script that individual gradle projects should include if it
|
||||||
|
has content that should be included in a distribution as an extension. Including this
|
||||||
|
will cause all the standard module files to be included in the build as a sub-zipped extension.
|
||||||
|
|
||||||
|
A gradle project can add itself as an extension to the build distribution by adding the
|
||||||
|
following to its build.gradle file:
|
||||||
|
|
||||||
|
apply from: "$rootProject.projectDir/gradle/support/distributableGhidraModule.gradle"
|
||||||
|
*****************************************************************************************/
|
||||||
|
|
||||||
|
apply from: "$rootProject.projectDir/gradle/distributableGhidraExtension.gradle"
|
||||||
|
|
||||||
|
zipExtensions {
|
||||||
|
def p = this.project
|
||||||
|
from (p.projectDir) {
|
||||||
|
exclude 'build/**'
|
||||||
|
exclude 'certification.manifest'
|
||||||
|
exclude "*.project"
|
||||||
|
exclude "*.classpath"
|
||||||
|
exclude '.settings/**'
|
||||||
|
exclude 'bin/**'
|
||||||
|
|
||||||
|
into { getBaseProjectName(p) }
|
||||||
|
}
|
||||||
|
}
|
@ -102,17 +102,17 @@ eclipse {
|
|||||||
type = 'EXCLUDE_ALL'
|
type = 'EXCLUDE_ALL'
|
||||||
matcher {
|
matcher {
|
||||||
id = 'org.eclipse.ui.ide.multiFilter'
|
id = 'org.eclipse.ui.ide.multiFilter'
|
||||||
arguments = '1.0-name-matches-false-true-CabExtract'
|
arguments = '1.0-name-matches-false-false-GhidraDocs'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
resourceFilter {
|
resourceFilter {
|
||||||
appliesTo = 'FILES_AND_FOLDERS'
|
appliesTo = 'FILES_AND_FOLDERS'
|
||||||
type = 'EXCLUDE_ALL'
|
type = 'EXCLUDE_ALL'
|
||||||
matcher {
|
matcher {
|
||||||
id = 'org.eclipse.ui.ide.multiFilter'
|
id = 'org.eclipse.ui.ide.multiFilter'
|
||||||
arguments = '1.0-name-matches-false-false-GhidraDocs'
|
arguments = '1.0-name-matches-false-true-CabExtract'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
resourceFilter {
|
resourceFilter {
|
||||||
appliesTo = 'FILES_AND_FOLDERS'
|
appliesTo = 'FILES_AND_FOLDERS'
|
||||||
type = 'EXCLUDE_ALL'
|
type = 'EXCLUDE_ALL'
|
||||||
@ -129,6 +129,14 @@ eclipse {
|
|||||||
arguments = '1.0-name-matches-false-false-DemanglerGnu'
|
arguments = '1.0-name-matches-false-false-DemanglerGnu'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
resourceFilter {
|
||||||
|
appliesTo = 'FILES_AND_FOLDERS'
|
||||||
|
type = 'EXCLUDE_ALL'
|
||||||
|
matcher {
|
||||||
|
id = 'org.eclipse.ui.ide.multiFilter'
|
||||||
|
arguments = '1.0-name-matches-false-true-GnuDisassembler'
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user