├── .antProperties.xml ├── .gitignore ├── .pydevproject ├── .version ├── LICENSE ├── Module.manifest ├── README.md ├── build.gradle ├── coveragetools ├── .gitignore ├── LICENSE ├── README.md ├── bconf.props ├── bconf_64.props ├── build.sh ├── ddph.c └── ddph.vcxproj ├── data ├── README.txt ├── languages │ ├── skel.cspec │ ├── skel.ldefs │ ├── skel.opinion │ ├── skel.pspec │ ├── skel.sinc │ └── skel.slaspec └── sleighArgs.txt ├── extension.properties ├── lib └── README.txt ├── os ├── linux64 │ └── README.txt ├── osx64 │ └── README.txt └── win64 │ └── README.txt └── src └── main ├── java └── dragondance │ ├── DragondancePlugin.java │ ├── DragondancePluginPackage.java │ ├── Globals.java │ ├── Log.java │ ├── StringResources.java │ ├── components │ ├── GuiAffectedOpInterface.java │ └── MainDockProvider.java │ ├── datasource │ ├── BlockEntry.java │ ├── CommonDatabaseDataSource.java │ ├── CoverageData.java │ ├── CoverageDataSource.java │ ├── DynamorioDataSource.java │ ├── ModuleInfo.java │ └── PintoolDataSource.java │ ├── eng │ ├── CodeRange.java │ ├── DragonHelper.java │ ├── InstructionContext.java │ ├── InstructionInfo.java │ ├── Painter.java │ └── session │ │ ├── Session.java │ │ └── SessionManager.java │ ├── exceptions │ ├── DragonDanceScriptRuntimeException.java │ ├── InvalidInstructionAddress.java │ ├── OperationAbortedException.java │ └── ScriptParserException.java │ ├── scripting │ ├── DragonDanceScriptParser.java │ ├── DragonDanceScripting.java │ ├── ScriptExecutionUnit.java │ ├── ScriptVariable.java │ └── functions │ │ ├── BuiltinAlias.java │ │ ├── BuiltinArg.java │ │ ├── BuiltinFunctionBase.java │ │ └── impl │ │ ├── BuiltinFunctionClear.java │ │ ├── BuiltinFunctionCwd.java │ │ ├── BuiltinFunctionDiff.java │ │ ├── BuiltinFunctionDiscard.java │ │ ├── BuiltinFunctionDistinct.java │ │ ├── BuiltinFunctionGoto.java │ │ ├── BuiltinFunctionImport.java │ │ ├── BuiltinFunctionIntersect.java │ │ ├── BuiltinFunctionShow.java │ │ └── BuiltinFunctionSum.java │ └── util │ ├── TextGraphic.java │ └── Util.java └── resources └── images └── README.txt /.antProperties.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | /*/*/*/*/*/bin/ 3 | /*/*/*/*/*/build/ 4 | /*/*/*/*/bin/ 5 | /*/*/*/*/build/ 6 | /*/*/*/bin/ 7 | /*/*/*/build/ 8 | /*/*/bin/ 9 | /*/*/build/ 10 | /*/bin/ 11 | /*/build/ 12 | /build/ 13 | /bin/ 14 | 15 | **/dist 16 | 17 | # Ignore Sleigh generated files 18 | *.sla 19 | **/data/build.xml 20 | 21 | # Misc files 22 | *.setting 23 | *.settings 24 | *.directory 25 | .gradle/ 26 | 27 | # File locks 28 | *.ulock 29 | *.lock 30 | 31 | # Ignore object files 32 | *.o 33 | *.obj 34 | *.class 35 | 36 | 37 | # Ignore UNIX backup files 38 | *~ 39 | *.swp 40 | 41 | # Ignore eclipse project files 42 | .project 43 | .classpath 44 | .settings/ -------------------------------------------------------------------------------- /.pydevproject: -------------------------------------------------------------------------------- 1 | 2 | 3 | Default 4 | python interpreter 5 | 6 | -------------------------------------------------------------------------------- /.version: -------------------------------------------------------------------------------- 1 | 0.2.2 -------------------------------------------------------------------------------- /Module.manifest: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/0ffffffffh/dragondance/19e2ecefe4a29e682dd571454cef05743d1f409d/Module.manifest -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | // Builds a Ghidra Extension for a given Ghidra installation. 2 | // 3 | // An absolute path to the Ghidra installation directory must be supplied either by setting the 4 | // GHIDRA_INSTALL_DIR environment variable or Gradle project property: 5 | // 6 | // > export GHIDRA_INSTALL_DIR= 7 | // > gradle 8 | // 9 | // or 10 | // 11 | // > gradle -PGHIDRA_INSTALL_DIR= 12 | // 13 | // Gradle should be invoked from the directory of the project to build. Please see the 14 | // application.gradle.version property in /Ghidra/application.properties 15 | // for the correction version of Gradle to use for the Ghidra installation you specify. 16 | 17 | //----------------------START "DO NOT MODIFY" SECTION------------------------------ 18 | def ghidraInstallDir 19 | 20 | if (System.env.GHIDRA_INSTALL_DIR) { 21 | ghidraInstallDir = System.env.GHIDRA_INSTALL_DIR 22 | } 23 | else if (project.hasProperty("GHIDRA_INSTALL_DIR")) { 24 | ghidraInstallDir = project.getProperty("GHIDRA_INSTALL_DIR") 25 | } 26 | 27 | if (ghidraInstallDir) { 28 | apply from: new File(ghidraInstallDir).getCanonicalPath() + "/support/buildExtension.gradle" 29 | } 30 | else { 31 | throw new GradleException("GHIDRA_INSTALL_DIR is not defined!") 32 | } 33 | //----------------------END "DO NOT MODIFY" SECTION------------------------------- 34 | -------------------------------------------------------------------------------- /coveragetools/.gitignore: -------------------------------------------------------------------------------- 1 | Debug 2 | Release 3 | x64 4 | *.filters 5 | *.user -------------------------------------------------------------------------------- /coveragetools/README.md: -------------------------------------------------------------------------------- 1 | ## DDPH (Dragon Dance Pin Helper) 2 | 3 | **ddph** is a module for Intel Pin that can collect and supply coverage data to the Dragon Dance plugin. You can find usage of this module on the Dragon Dance's readme section. 4 | 5 | dpph comes with its build shell script for macOS, Linux. And comes with a Visual C++ project file for Windows to build with msbuild tool or directly from the Visual Studio. 6 | 7 | If you don't want to waste your time with building it from the source, you can find the binaries for Windows, Linux and macOS. 8 | 9 | ### Building from the source 10 | 11 | ##### For Windows: 12 | 13 | ddph comes with Visual C++ Project file and two props file which are contained macro variable definition for the project. That means you can build with Visual Studio or msbuild command line tool. 14 | 15 | *Preparation:* 16 | 17 | Before building the source you have to set **PIN_ROOT** environment variable with the Intel Pin dev kit's root directory. Otherwise compiler could not locate the required headers and the libraries. 18 | 19 | Then you can load into Visual Studio via click the vcxproj file and build within GUI, or you can use msbuild. 20 | 21 | Building the ddph with the msbuild is not complicated. Open a command prompt. Set current directory to the ddph's source folder. 22 | 23 | Then type following command 24 | 25 | `msbuild ddph.vcxproj /p:Configuration=[BUILD_TYPE] /p:Platform=[ARCHITECTURE]` 26 | 27 | *BUILD_TYPE* can be "Debug" or "Release" 28 | *ARCHITECTURE* can be "x64" for 64 bit compilation, "Win32" for 32 bit compilation. That's all. 29 | 30 | 31 | 32 | ![](https://user-images.githubusercontent.com/437161/57917575-a41be180-789d-11e9-9478-ca43eff54512.gif) 33 | 34 | 35 | 36 | ##### For Linux and macOS 37 | 38 | ddph also comes with a build shell script (**build.sh**) for Linux and macOS. You can execute the script thats all. 39 | 40 | *Preparation:* 41 | 42 | Before execute the build script you have to set script variable with pin kit directory. Open build.sh and change PIN_ROOT variable with your valid intel pin dev kit root path. 43 | 44 | Also you make sure that it has proper permission to execute. (chmod +x build.sh) 45 | 46 | If everything is ok, simply execute the build.sh. build script can take arguments for build operation. 47 | 48 | `./build.sh [ARCHITECTURE] [OPTIONAL_FLAGS]` 49 | 50 | *ARCHITECTURE* can be "x32" for 32 bit or "x64" for 64 bit compilation. If you are running on 64 bit os but want to 32 bit compilation you have to aware to cross compilation thing. Build script checks system's arch and sets -m32 flag if OS arch is 64 bit and compilation arch is x32. 51 | 52 | *OPTIONAL_FLAGS* this flag can be only **-oldabi**. If your C++ compiler does not met Pin dev kit's ABI , you may get in trouble with the compilation. The C++11 ABI changes breaks the ABI compatibility of the binary. If you are in such a situation, your compilation will be broken with an error that is says there is an ABI problem. To handle that problem, build script has a flag to force the compiler to use older ABI. To get more information about the ABI changes you can follow this [link](https://gcc.gnu.org/onlinedocs/libstdc++/manual/using_dual_abi.html) 53 | 54 | ![](https://user-images.githubusercontent.com/437161/57917571-a1b98780-789d-11e9-8c89-0c7feb6c0229.gif) 55 | 56 | ![](https://user-images.githubusercontent.com/437161/57917565-9ebe9700-789d-11e9-8a10-1dc6efc965af.gif) 57 | 58 | You can get some warnings during the compilation but that's ok, these are noisy and ignorable. 59 | 60 | -------------------------------------------------------------------------------- /coveragetools/bconf.props: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | ia32 6 | 32 7 | x86 8 | 9 | 10 | <_PropertySheetDisplayName>bconf_32 11 | 12 | 13 | 14 | 15 | $(TARGET) 16 | 17 | 18 | $(BITNESS) 19 | 20 | 21 | $(BIONIC_ARCH) 22 | 23 | 24 | -------------------------------------------------------------------------------- /coveragetools/bconf_64.props: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | intel64 6 | 64 7 | x86_64 8 | 9 | 10 | 11 | 12 | 13 | $(TARGET) 14 | 15 | 16 | $(BITNESS) 17 | 18 | 19 | $(BIONIC_ARCH) 20 | 21 | 22 | -------------------------------------------------------------------------------- /coveragetools/build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | #set your own pin root directory 4 | PIN_ROOT=/home/kartal/pin 5 | 6 | CL_DEFS="-Wno-unknown-pragmas -D__PIN__=1 -DPIN_CRT=1" 7 | CL_FLAG="-fno-exceptions -funwind-tables -fno-rtti -fno-stack-protector -fpermissive -fPIC " 8 | LD_FLAG="" 9 | 10 | CL_LINKER_LIBS="-lpin -lxed -lc-dynamic -lm-dynamic -lstlport-dynamic -lpin3dwarf -lunwind-dynamic" 11 | 12 | 13 | INC_BASE="-isystem $PIN_ROOT" 14 | 15 | 16 | if [ "$1" = "x32" ]; then 17 | TARGET=ia32 18 | BIONIC_ARCH=x86 19 | ARCH_SUFFIX="32" 20 | CL_DEFS="$CL_DEFS -DTARGET_IA32 -D__i386__ -DHOST_IA32" 21 | 22 | if [ $(uname -m | grep "64") != "" ]; then 23 | echo "Host system is 64 bit but 32 bit compilation requested. So added -m32 flag to achieve the cross compiling" 24 | CL_FLAG="-m32 $CL_FLAG" 25 | LD_FLAG="-m32" 26 | fi 27 | else 28 | TARGET=intel64 29 | ARCH_SUFFIX="64" 30 | BIONIC_ARCH=x86_64 31 | CL_DEFS="$CL_DEFS -DTARGET_IA32E -D__x86_64__ -DHOST_IA32E" 32 | fi 33 | 34 | 35 | PINCRT_ROOT="$PIN_ROOT/$TARGET/runtime/pincrt" 36 | 37 | if [ "$(g++ -v 2>&1 | grep 'Apple')" != "" ]; then 38 | COMP_SPECIFIC="-Wl,-no_new_main" 39 | else 40 | COMP_SPECIFIC="-Wl,--hash-style=sysv -Wl,-Bsymbolic" 41 | fi 42 | 43 | if [ "$2" = "-oldabi" ]; then 44 | #GXX's new c++ abi standard breaks intel pin sdk build. 45 | #so we need to set older version of the ABI. 46 | CL_DEFS="$CL_DEFS -D_GLIBCXX_USE_CXX11_ABI=0" 47 | CL_FLAG="$CL_FLAG -fabi-version=2" 48 | LD_FLAG="$LD_FLAG -fabi-version=2" 49 | fi 50 | 51 | if [ "$(uname -s)" = 'Darwin' ]; then 52 | BIN_SUFFIX="dylib" 53 | CL_DEFS="$CL_DEFS -DTARGET_MAC -D__DARWIN_ONLY_UNIX_CONFORMANCE=1 -D__DARWIN_UNIX03=0" 54 | else 55 | CL_DEFS="$CL_DEFS -DTARGET_LINUX" 56 | BIN_SUFFIX="so" 57 | LD_FLAG="$LD_FLAG -Wl,$PINCRT_ROOT/crtendS.o" 58 | CL_LINKER_LIBS="$CL_LINKER_LIBS -ldl-dynamic" 59 | fi 60 | 61 | 62 | CL_LINKER_LIB_DIRS="-L$PIN_ROOT/$TARGET/runtime/pincrt -L$PIN_ROOT/extras/xed-$TARGET/lib -L$PIN_ROOT/$TARGET/lib -L$PIN_ROOT/$TARGET/lib-ext" 63 | 64 | CL_LINKER="-nostdlib $CL_LINKER_LIB_DIRS $CL_LINKER_LIBS" 65 | 66 | 67 | INCLUDES="$INC_BASE/source/include/pin/gen " 68 | INCLUDES="$INCLUDES $INC_BASE/source/include/pin " 69 | INCLUDES="$INCLUDES $INC_BASE/extras/stlport/include " 70 | INCLUDES="$INCLUDES $INC_BASE/extras/libstdc++/include " 71 | INCLUDES="$INCLUDES $INC_BASE/extras/crt/include " 72 | INCLUDES="$INCLUDES $INC_BASE/extras/crt/include/arch-$BIONIC_ARCH " 73 | INCLUDES="$INCLUDES $INC_BASE/extras/crt/include/kernel/uapi " 74 | INCLUDES="$INCLUDES $INC_BASE/extras/crt/include/kernel/uapi/asm-x86 " 75 | INCLUDES="$INCLUDES $INC_BASE/extras/components/include " 76 | INCLUDES="$INCLUDES $INC_BASE/extras/xed-$TARGET/include/xed " 77 | 78 | #compile source without linking 79 | eval "g++ $CL_DEFS $CL_FLAG $INCLUDES -c -o ddph.o ddph.c" 80 | 81 | #link main program object file with required intel pin shared libs and build .so 82 | eval "g++ -shared $COMP_SPECIFIC -Wl,$PINCRT_ROOT/crtbeginS.o -o ddph$ARCH_SUFFIX.$BIN_SUFFIX ddph.o $CL_LINKER $LD_FLAG" 83 | 84 | -------------------------------------------------------------------------------- /coveragetools/ddph.vcxproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | Win32 7 | 8 | 9 | Release 10 | Win32 11 | 12 | 13 | Debug 14 | x64 15 | 16 | 17 | Release 18 | x64 19 | 20 | 21 | 22 | 23 | 24 | 25 | 15.0 26 | {82FCCB7E-BEDD-4F69-BBE2-EF78DD783CB7} 27 | Win32Proj 28 | ddph 29 | 10.0.17134.0 30 | 31 | 32 | 33 | DynamicLibrary 34 | true 35 | v141 36 | Unicode 37 | 38 | 39 | DynamicLibrary 40 | false 41 | v141 42 | true 43 | Unicode 44 | 45 | 46 | DynamicLibrary 47 | true 48 | v141 49 | Unicode 50 | 51 | 52 | DynamicLibrary 53 | false 54 | v141 55 | true 56 | Unicode 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | true 82 | $(PIN_ROOT)extras\stlport\include;$(PIN_ROOT)\extras;$(PIN_ROOT)\extras\xed-$(TARGET)\include\xed;$(PIN_ROOT)\extras\components\include;$(PIN_ROOT)\extras\libstdc++\include;$(PIN_ROOT)extras\crt\include;$(PIN_ROOT)extras\crt;$(PIN_ROOT)extra\crt\include\arch-$(BIONIC_ARCH);$(PIN_ROOT)extras\crt\include\kernel\uapi;$(PIN_ROOT)extras\crt\include\kernel\uapi\asm-x86;$(PIN_ROOT)source\include\pin;$(PIN_ROOT)source\include\pin\gen 83 | 84 | 85 | $(MSBuildProjectDirectory)\_build 86 | $(ProjectName)$(BITNESS) 87 | junks 88 | 89 | 90 | true 91 | $(PIN_ROOT)extras\stlport\include;$(PIN_ROOT)\extras;$(PIN_ROOT)\extras\xed-$(TARGET)\include\xed;$(PIN_ROOT)\extras\components\include;$(PIN_ROOT)\extras\libstdc++\include;$(PIN_ROOT)extras\crt\include;$(PIN_ROOT)extras\crt;$(PIN_ROOT)extra\crt\include\arch-$(BIONIC_ARCH);$(PIN_ROOT)extras\crt\include\kernel\uapi;$(PIN_ROOT)extras\crt\include\kernel\uapi\asm-x86;$(PIN_ROOT)source\include\pin;$(PIN_ROOT)source\include\pin\gen 92 | 93 | 94 | 95 | $(MSBuildProjectDirectory)\_build 96 | $(ProjectName)$(BITNESS) 97 | junks 98 | 99 | 100 | false 101 | $(PIN_ROOT)extras\stlport\include;$(PIN_ROOT)\extras;$(PIN_ROOT)\extras\xed-$(TARGET)\include\xed;$(PIN_ROOT)\extras\components\include;$(PIN_ROOT)\extras\libstdc++\include;$(PIN_ROOT)extras\crt\include;$(PIN_ROOT)extras\crt;$(PIN_ROOT)extra\crt\include\arch-$(BIONIC_ARCH);$(PIN_ROOT)extras\crt\include\kernel\uapi;$(PIN_ROOT)extras\crt\include\kernel\uapi\asm-x86;$(PIN_ROOT)source\include\pin;$(PIN_ROOT)source\include\pin\gen 102 | 103 | 104 | $(MSBuildProjectDirectory)\_build 105 | $(ProjectName)$(BITNESS) 106 | junks 107 | 108 | 109 | false 110 | $(PIN_ROOT)extras\stlport\include;$(PIN_ROOT)\extras;$(PIN_ROOT)\extras\xed-$(TARGET)\include\xed;$(PIN_ROOT)\extras\components\include;$(PIN_ROOT)\extras\libstdc++\include;$(PIN_ROOT)extras\crt\include;$(PIN_ROOT)extras\crt;$(PIN_ROOT)extra\crt\include\arch-$(BIONIC_ARCH);$(PIN_ROOT)extras\crt\include\kernel\uapi;$(PIN_ROOT)extras\crt\include\kernel\uapi\asm-x86;$(PIN_ROOT)source\include\pin;$(PIN_ROOT)source\include\pin\gen 111 | 112 | 113 | 114 | $(MSBuildProjectDirectory)\_build 115 | $(ProjectName)$(BITNESS) 116 | junks 117 | 118 | 119 | 120 | NotUsing 121 | Level3 122 | Disabled 123 | true 124 | WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) 125 | true 126 | 127 | 128 | /D__PIN__=1 /DPIN_CRT=1 /DTARGET_WINDOWS /DHOST_IA32 /FIinclude/msvc_compat.h /DTARGET_IA32 /D__i386__ /GR- /GS- /EHs- /EHa- /FP:strict /Oi- %(AdditionalOptions) 129 | $(PIN_ROOT)extras\stlport\include;$(PIN_ROOT)\extras;$(PIN_ROOT)\extras\xed-$(TARGET)\include\xed;$(PIN_ROOT)\extras\components\include;$(PIN_ROOT)\extras\libstdc++\include;$(PIN_ROOT)extras\crt\include;$(PIN_ROOT)extras\crt;$(PIN_ROOT)extra\crt\include\arch-$(BIONIC_ARCH);$(PIN_ROOT)extras\crt\include\kernel\uapi;$(PIN_ROOT)extras\crt\include\kernel\uapi\asm-x86;$(PIN_ROOT)source\include\pin;$(PIN_ROOT)source\include\pin\gen 130 | CompileAsCpp 131 | true 132 | false 133 | 134 | 135 | 136 | NotSet 137 | true 138 | /NODEFAULTLIB /LIBPATH:$(PIN_ROOT)/$(TARGET)runtime\pincrt /LIBPATH:$(PIN_ROOT)$(TARGET)\lib-ext /IGNORE:4210 /export:main /IGNORE:4049 /export:main %(AdditionalOptions) 139 | $(PIN_ROOT)$(TARGET)\runtime\pincrt;$(PIN_ROOT)$(TARGET)\lib;$(PIN_ROOT)$(TARGET)\lib-ext;$(PIN_ROOT)extras\xed-$(TARGET)\lib;%(AdditionalLibraryDirectories) 140 | crtbeginS.obj;pin.lib;xed.lib;pinvm.lib;kernel32.lib;stlport-static.lib;m-static.lib;c-static.lib;os-apis.lib;ntdll-$(BITNESS).lib;%(AdditionalDependencies) 141 | true 142 | Ptrace_DllMainCRTStartup@12 143 | 0x55000000 144 | false 145 | 146 | 147 | COPY $(MSBuildProjectDirectory)\_build\$(TargetFileName) $(MSBuildProjectDirectory)\$(TargetFileName) 148 | DEL $(MSBuildProjectDirectory)\_build\*.* /S /Q 149 | DEL $(MSBuildProjectDirectory)\junks\*.* /S /Q 150 | RMDIR /S /Q $(MSBuildProjectDirectory)\_build 151 | RMDIR /S /Q $(MSBuildProjectDirectory)\junks 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | NotUsing 160 | Level3 161 | Disabled 162 | true 163 | _DEBUG;_CONSOLE;%(PreprocessorDefinitions) 164 | true 165 | 166 | 167 | /D__PIN__=1 /DPIN_CRT=1 /DTARGET_WINDOWS /DHOST_IA32E /FIinclude/msvc_compat.h /DTARGET_IA32E /D__LP64__ /GR- /GS- /EHs- /EHa- /FP:strict /Oi- %(AdditionalOptions) 168 | $(PIN_ROOT)extras\stlport\include;$(PIN_ROOT)\extras;$(PIN_ROOT)\extras\xed-$(TARGET)\include\xed;$(PIN_ROOT)\extras\components\include;$(PIN_ROOT)\extras\libstdc++\include;$(PIN_ROOT)extras\crt\include;$(PIN_ROOT)extras\crt;$(PIN_ROOT)extra\crt\include\arch-$(BIONIC_ARCH);$(PIN_ROOT)extras\crt\include\kernel\uapi;$(PIN_ROOT)extras\crt\include\kernel\uapi\asm-x86;$(PIN_ROOT)source\include\pin;$(PIN_ROOT)source\include\pin\gen 169 | CompileAsCpp 170 | true 171 | false 172 | 173 | 174 | 175 | NotSet 176 | true 177 | /NODEFAULTLIB /LIBPATH:$(PIN_ROOT)/$(TARGET)runtime\pincrt /LIBPATH:$(PIN_ROOT)$(TARGET)\lib-ext /IGNORE:4210 /export:main /IGNORE:4049 %(AdditionalOptions) 178 | $(PIN_ROOT)$(TARGET)\runtime\pincrt;$(PIN_ROOT)$(TARGET)\lib;$(PIN_ROOT)$(TARGET)\lib-ext;$(PIN_ROOT)extras\xed-$(TARGET)\lib;%(AdditionalLibraryDirectories) 179 | crtbeginS.obj;pin.lib;xed.lib;pinvm.lib;kernel32.lib;stlport-static.lib;m-static.lib;c-static.lib;os-apis.lib;ntdll-$(BITNESS).lib;%(AdditionalDependencies) 180 | true 181 | Ptrace_DllMainCRTStartup 182 | 0xC500000000000000 183 | false 184 | 185 | 186 | COPY $(MSBuildProjectDirectory)\_build\$(TargetFileName) $(MSBuildProjectDirectory)\$(TargetFileName) 187 | DEL $(MSBuildProjectDirectory)\_build\*.* /S /Q 188 | DEL $(MSBuildProjectDirectory)\junks\*.* /S /Q 189 | RMDIR /S /Q $(MSBuildProjectDirectory)\_build 190 | RMDIR /S /Q $(MSBuildProjectDirectory)\junks 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | NotUsing 199 | Level3 200 | MaxSpeed 201 | true 202 | true 203 | true 204 | WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) 205 | true 206 | 207 | 208 | /D__PIN__=1 /DPIN_CRT=1 /DTARGET_WINDOWS /DHOST_IA32 /FIinclude/msvc_compat.h /DTARGET_IA32 /D__i386__ /GR- /GS- /EHs- /EHa- /FP:strict /Oi- %(AdditionalOptions) 209 | $(PIN_ROOT)extras\stlport\include;$(PIN_ROOT)\extras;$(PIN_ROOT)\extras\xed-$(TARGET)\include\xed;$(PIN_ROOT)\extras\components\include;$(PIN_ROOT)\extras\libstdc++\include;$(PIN_ROOT)extras\crt\include;$(PIN_ROOT)extras\crt;$(PIN_ROOT)extra\crt\include\arch-$(BIONIC_ARCH);$(PIN_ROOT)extras\crt\include\kernel\uapi;$(PIN_ROOT)extras\crt\include\kernel\uapi\asm-x86;$(PIN_ROOT)source\include\pin;$(PIN_ROOT)source\include\pin\gen 210 | CompileAsCpp 211 | true 212 | 213 | 214 | 215 | NotSet 216 | true 217 | true 218 | true 219 | /NODEFAULTLIB /LIBPATH:$(PIN_ROOT)/$(TARGET)runtime\pincrt /LIBPATH:$(PIN_ROOT)$(TARGET)\lib-ext /IGNORE:4210 /export:main /IGNORE:4049 /export:main %(AdditionalOptions) 220 | $(PIN_ROOT)$(TARGET)\runtime\pincrt;$(PIN_ROOT)$(TARGET)\lib;$(PIN_ROOT)$(TARGET)\lib-ext;$(PIN_ROOT)extras\xed-$(TARGET)\lib;%(AdditionalLibraryDirectories) 221 | crtbeginS.obj;pin.lib;xed.lib;pinvm.lib;kernel32.lib;stlport-static.lib;m-static.lib;c-static.lib;os-apis.lib;ntdll-$(BITNESS).lib;%(AdditionalDependencies) 222 | true 223 | Ptrace_DllMainCRTStartup@12 224 | 0x55000000 225 | false 226 | 227 | 228 | COPY $(MSBuildProjectDirectory)\_build\$(TargetFileName) $(MSBuildProjectDirectory)\$(TargetFileName) 229 | DEL $(MSBuildProjectDirectory)\_build\*.* /S /Q 230 | DEL $(MSBuildProjectDirectory)\junks\*.* /S /Q 231 | RMDIR /S /Q $(MSBuildProjectDirectory)\_build 232 | RMDIR /S /Q $(MSBuildProjectDirectory)\junks 233 | 234 | 235 | 236 | 237 | 238 | 239 | 240 | NotUsing 241 | Level3 242 | MaxSpeed 243 | true 244 | true 245 | true 246 | NDEBUG;_CONSOLE;%(PreprocessorDefinitions) 247 | true 248 | 249 | 250 | /D__PIN__=1 /DPIN_CRT=1 /DTARGET_WINDOWS /DHOST_IA32E /FIinclude/msvc_compat.h /DTARGET_IA32E /D__LP64__ /GR- /GS- /EHs- /EHa- /FP:strict /Oi- %(AdditionalOptions) 251 | $(PIN_ROOT)extras\stlport\include;$(PIN_ROOT)\extras;$(PIN_ROOT)\extras\xed-$(TARGET)\include\xed;$(PIN_ROOT)\extras\components\include;$(PIN_ROOT)\extras\libstdc++\include;$(PIN_ROOT)extras\crt\include;$(PIN_ROOT)extras\crt;$(PIN_ROOT)extra\crt\include\arch-$(BIONIC_ARCH);$(PIN_ROOT)extras\crt\include\kernel\uapi;$(PIN_ROOT)extras\crt\include\kernel\uapi\asm-x86;$(PIN_ROOT)source\include\pin;$(PIN_ROOT)source\include\pin\gen 252 | CompileAsCpp 253 | true 254 | 255 | 256 | 257 | NotSet 258 | true 259 | true 260 | true 261 | /NODEFAULTLIB /LIBPATH:$(PIN_ROOT)/$(TARGET)runtime\pincrt /LIBPATH:$(PIN_ROOT)$(TARGET)\lib-ext /IGNORE:4210 /export:main /IGNORE:4049 %(AdditionalOptions) 262 | $(PIN_ROOT)$(TARGET)\runtime\pincrt;$(PIN_ROOT)$(TARGET)\lib;$(PIN_ROOT)$(TARGET)\lib-ext;$(PIN_ROOT)extras\xed-$(TARGET)\lib;%(AdditionalLibraryDirectories) 263 | crtbeginS.obj;pin.lib;xed.lib;pinvm.lib;kernel32.lib;stlport-static.lib;m-static.lib;c-static.lib;os-apis.lib;ntdll-$(BITNESS).lib;%(AdditionalDependencies) 264 | true 265 | Ptrace_DllMainCRTStartup 266 | 0xC500000000000000 267 | false 268 | 269 | 270 | COPY $(MSBuildProjectDirectory)\_build\$(TargetFileName) $(MSBuildProjectDirectory)\$(TargetFileName) 271 | DEL $(MSBuildProjectDirectory)\_build\*.* /S /Q 272 | DEL $(MSBuildProjectDirectory)\junks\*.* /S /Q 273 | RMDIR /S /Q $(MSBuildProjectDirectory)\_build 274 | RMDIR /S /Q $(MSBuildProjectDirectory)\junks 275 | 276 | 277 | 278 | 279 | 280 | 281 | 282 | 283 | -------------------------------------------------------------------------------- /data/README.txt: -------------------------------------------------------------------------------- 1 | The "data" directory is intended to hold data files that will be used by this module and will 2 | not end up in the .jar file, but will be present in the zip or tar file. Typically, data 3 | files are placed here rather than in the resources directory if the user may need to edit them. 4 | 5 | An optional data/languages directory can exist for the purpose of containing various Sleigh language 6 | specification files and importer opinion files. 7 | 8 | The data/build.xml is used for building the contents of the data/languages directory. 9 | 10 | The skel language definition has been commented-out within the skel.ldefs file so that the 11 | skeleton language does not show-up within Ghidra. 12 | 13 | See the Sleigh language documentation (docs/languages/sleigh.htm or sleigh.pdf) for details 14 | on Sleigh language specification syntax. 15 | 16 | -------------------------------------------------------------------------------- /data/languages/skel.cspec: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | -------------------------------------------------------------------------------- /data/languages/skel.ldefs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 20 | 21 | -------------------------------------------------------------------------------- /data/languages/skel.opinion: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 12 | 13 | -------------------------------------------------------------------------------- /data/languages/skel.pspec: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /data/languages/skel.sinc: -------------------------------------------------------------------------------- 1 | # sleigh include file for Skeleton language instructions 2 | 3 | define token opbyte (8) 4 | op0_8 = (0,7) 5 | op6_2 = (6,7) 6 | 7 | dRegPair4_2 = (4,5) 8 | pRegPair4_2 = (4,5) 9 | sRegPair4_2 = (4,5) 10 | qRegPair4_2 = (4,5) 11 | qRegPair4_2a = (4,5) 12 | qRegPair4_2b = (4,5) 13 | rRegPair4_2 = (4,5) 14 | 15 | reg3_3 = (3,5) 16 | bits3_3 = (3,5) 17 | 18 | bits0_4 = (0,3) 19 | 20 | reg0_3 = (0,2) 21 | bits0_3 = (0,2) 22 | ; 23 | 24 | define token data8 (8) 25 | imm8 = (0,7) 26 | sign8 = (7,7) 27 | simm8 = (0,7) signed 28 | ; 29 | 30 | define token data16 (16) 31 | timm4 = (12,15) 32 | imm16 = (0,15) 33 | sign16 = (15,15) 34 | simm16 = (0,15) signed 35 | ; 36 | 37 | attach variables [ reg0_3 reg3_3 ] [ B C D E H L _ A ]; 38 | 39 | attach variables [ sRegPair4_2 dRegPair4_2 ] [ BC DE HL SP ]; 40 | 41 | attach variables [ qRegPair4_2 ] [ BC DE HL AF ]; 42 | attach variables [ qRegPair4_2a ] [ B D H A ]; 43 | attach variables [ qRegPair4_2b ] [ C E L F ]; 44 | 45 | attach variables [ pRegPair4_2 ] [ BC DE IX SP ]; 46 | attach variables [ rRegPair4_2 ] [ BC DE IY SP ]; 47 | 48 | ################################################################ 49 | # Macros 50 | ################################################################ 51 | 52 | macro setResultFlags(result) { 53 | $(Z_flag) = (result == 0); 54 | $(S_flag) = (result s< 0); 55 | } 56 | 57 | macro setAddCarryFlags(op1,op2) { 58 | $(C_flag) = (carry(op1,zext($(C_flag))) || carry(op2,op1 + zext($(C_flag)))); 59 | } 60 | 61 | macro setAddFlags(op1,op2) { 62 | $(C_flag) = carry(op1,op2); 63 | } 64 | 65 | macro setSubtractCarryFlags(op1,op2) { 66 | notC = ~$(C_flag); 67 | $(C_flag) = ((op1 < sext(notC)) || (op2 < (op1 - sext(notC)))); 68 | } 69 | 70 | macro setSubtractFlags(op1,op2) { 71 | $(C_flag) = (op1 < op2); 72 | } 73 | 74 | macro push16(val16) { 75 | SP = SP - 2; 76 | *:2 SP = val16; 77 | } 78 | 79 | macro pop16(ret16) { 80 | ret16 = *:2 SP; 81 | SP = SP + 2; 82 | } 83 | 84 | macro push8(val8) { 85 | SP = SP - 1; 86 | ptr:2 = SP; 87 | *:1 ptr = val8; 88 | } 89 | 90 | macro pop8(ret8) { 91 | ptr:2 = SP; 92 | ret8 = *:1 ptr; 93 | SP = SP + 1; 94 | } 95 | 96 | ################################################################ 97 | 98 | ixMem8: (IX+simm8) is IX & simm8 { ptr:2 = IX + simm8; export *:1 ptr; } 99 | ixMem8: (IX-val) is IX & simm8 & sign8=1 [ val = -simm8; ] { ptr:2 = IX + simm8; export *:1 ptr; } 100 | 101 | iyMem8: (IY+simm8) is IY & simm8 { ptr:2 = IY + simm8; export *:1 ptr; } 102 | iyMem8: (IY-val) is IY & simm8 & sign8=1 [ val = -simm8; ] { ptr:2 = IY + simm8; export *:1 ptr; } 103 | 104 | Addr16: imm16 is imm16 { export *:1 imm16; } 105 | 106 | Mem16: (imm16) is imm16 { export *:2 imm16; } 107 | 108 | RelAddr8: loc is simm8 [ loc = inst_next + simm8; ] { export *:1 loc; } 109 | 110 | cc: "NZ" is bits3_3=0x0 { c:1 = ($(Z_flag) == 0); export c; } 111 | cc: "Z" is bits3_3=0x1 { c:1 = $(Z_flag); export c; } 112 | cc: "NC" is bits3_3=0x2 { c:1 = ($(C_flag) == 0); export c; } 113 | cc: "C" is bits3_3=0x3 { c:1 = $(C_flag); export c; } 114 | cc: "PO" is bits3_3=0x4 { c:1 = ($(PV_flag) == 0); export c; } 115 | cc: "PE" is bits3_3=0x5 { c:1 = $(PV_flag); export c; } 116 | cc: "P" is bits3_3=0x6 { c:1 = ($(S_flag) == 0); export c; } 117 | cc: "M" is bits3_3=0x7 { c:1 = $(S_flag); export c; } 118 | 119 | cc2: "NZ" is bits3_3=0x4 { c:1 = ($(Z_flag) == 0); export c; } 120 | cc2: "Z" is bits3_3=0x5 { c:1 = $(Z_flag); export c; } 121 | cc2: "NC" is bits3_3=0x6 { c:1 = ($(C_flag) == 0); export c; } 122 | cc2: "C" is bits3_3=0x7 { c:1 = $(C_flag); export c; } 123 | 124 | ################################################################ 125 | 126 | 127 | :LD IX,Mem16 is op0_8=0xdd & IX; op0_8=0x2a; Mem16 { 128 | IX = Mem16; 129 | } 130 | 131 | :LD IY,Mem16 is op0_8=0xfd & IY; op0_8=0x2a; Mem16 { 132 | IY = Mem16; 133 | } 134 | 135 | :LD Mem16,HL is op0_8=0x22 & HL; Mem16 { 136 | Mem16 = HL; 137 | } 138 | 139 | :LD Mem16,dRegPair4_2 is op0_8=0xed; op6_2=0x1 & dRegPair4_2 & bits0_4=0x3; Mem16 { 140 | Mem16 = dRegPair4_2; 141 | } 142 | 143 | :LD Mem16,IX is op0_8=0xdd & IX; op0_8=0x22; Mem16 { 144 | Mem16 = IX; 145 | } 146 | 147 | :LD Mem16,IY is op0_8=0xfd & IY; op0_8=0x22; Mem16 { 148 | Mem16 = IY; 149 | } 150 | 151 | :NEG is op0_8=0xed; op0_8=0x44 { 152 | $(PV_flag) = (A == 0x80); 153 | $(C_flag) = (A != 0); 154 | A = -A; 155 | setResultFlags(A); 156 | } 157 | 158 | :SET bits3_3,ixMem8 is op0_8=0xdd; op0_8=0xcb; ixMem8; op6_2=0x3 & bits3_3 & bits0_3=0x6 { 159 | mask:1 = (1 << bits3_3); 160 | val:1 = ixMem8; 161 | ixMem8 = val | mask; 162 | } 163 | 164 | :SET bits3_3,iyMem8 is op0_8=0xfd; op0_8=0xcb; iyMem8; op6_2=0x3 & bits3_3 & bits0_3=0x6 { 165 | mask:1 = (1 << bits3_3); 166 | val:1 = iyMem8; 167 | iyMem8 = val | mask; 168 | } 169 | 170 | :JP Addr16 is op0_8=0xc3; Addr16 { 171 | goto Addr16; 172 | } 173 | 174 | :JP cc,Addr16 is op6_2=0x3 & cc & bits0_3=0x2; Addr16 { 175 | if (!cc) goto Addr16; 176 | } 177 | 178 | :JR RelAddr8 is op0_8=0x18; RelAddr8 { 179 | goto RelAddr8; 180 | } 181 | 182 | :JR cc2,RelAddr8 is op6_2=0x0 & cc2 & bits0_3=0x0; RelAddr8 { 183 | if (cc2) goto RelAddr8; 184 | } 185 | 186 | :JP (HL) is op0_8=0xe9 & HL { 187 | goto [HL]; 188 | } 189 | 190 | :JP (IX) is op0_8=0xdd & IX; op0_8=0xe9 { 191 | goto [IX]; 192 | } 193 | 194 | :JP (IY) is op0_8=0xfd & IY; op0_8=0xe9 { 195 | goto [IY]; 196 | } 197 | 198 | :CALL Addr16 is op0_8=0xcd; Addr16 { 199 | push16(&:2 inst_next); 200 | call Addr16; 201 | } 202 | 203 | :CALL cc,Addr16 is op6_2=0x3 & cc & bits0_3=0x4; Addr16 { 204 | if (!cc) goto inst_next; 205 | push16(&:2 inst_next); 206 | call Addr16; 207 | } 208 | 209 | :RET is op0_8=0xc9 { 210 | pop16(PC); 211 | ptr:2 = zext(PC); 212 | return [ptr]; 213 | } 214 | 215 | :RET cc is op6_2=0x3 & cc & bits0_3=0x0 { 216 | if (!cc) goto inst_next; 217 | pop16(PC); 218 | ptr:2 = zext(PC); 219 | return [ptr]; 220 | } 221 | -------------------------------------------------------------------------------- /data/languages/skel.slaspec: -------------------------------------------------------------------------------- 1 | # sleigh specification file for Skeleton Processor 2 | # >> see docs/languages/sleigh.htm or sleigh.pdf for Sleigh syntax 3 | # Other language modules (see Ghidra/Processors) may provide better examples 4 | # when creating a new language module. 5 | 6 | define endian=little; 7 | define alignment=1; 8 | 9 | define space ram type=ram_space size=2 default; 10 | 11 | define space io type=ram_space size=2; 12 | define space register type=register_space size=1; 13 | 14 | define register offset=0x00 size=1 [ F A C B E D L H I R ]; 15 | define register offset=0x00 size=2 [ AF BC DE HL ]; 16 | define register offset=0x20 size=1 [ A_ F_ B_ C_ D_ E_ H_ L_ ]; # Alternate registers 17 | define register offset=0x20 size=2 [ AF_ BC_ DE_ HL_ ]; # Alternate registers 18 | 19 | define register offset=0x40 size=2 [ _ PC SP IX IY ]; 20 | 21 | define register offset=0x50 size=1 [ rCBAR rCBR rBBR ]; 22 | 23 | # Define context bits (if defined, size must be multiple of 4-bytes) 24 | define register offset=0xf0 size=4 contextreg; 25 | 26 | define context contextreg 27 | assume8bitIOSpace = (0,0) 28 | ; 29 | 30 | # Flag bits (?? manual is very confusing - could be typos!) 31 | @define C_flag "F[0,1]" # C: Carry 32 | @define N_flag "F[1,1]" # N: Add/Subtract 33 | @define PV_flag "F[2,1]" # PV: Parity/Overflow 34 | @define H_flag "F[4,1]" # H: Half Carry 35 | @define Z_flag "F[6,1]" # Z: Zero 36 | @define S_flag "F[7,1]" # S: Sign 37 | 38 | # Include contents of skel.sinc file 39 | @include "skel.sinc" 40 | -------------------------------------------------------------------------------- /data/sleighArgs.txt: -------------------------------------------------------------------------------- 1 | # Add sleigh compiler options to this file (one per line) which will 2 | # be used when compiling each language within this module. 3 | # All options should start with a '-' character. 4 | # 5 | # IMPORTANT: The -a option should NOT be specified 6 | # -------------------------------------------------------------------------------- /extension.properties: -------------------------------------------------------------------------------- 1 | name=Dragon Dance 2 | description=Coverage data visualizer plugin. 3 | author=Oguz Kartal 4 | createdOn= 5 | version=@extversion@ 6 | -------------------------------------------------------------------------------- /lib/README.txt: -------------------------------------------------------------------------------- 1 | The "lib" directory is intended to hold Jar files which this module 2 | is dependent upon. This directory may be eliminated from a specific 3 | module if no other Jar files are needed. 4 | -------------------------------------------------------------------------------- /os/linux64/README.txt: -------------------------------------------------------------------------------- 1 | The "os/linux64" directory is intended to hold Linux native binaries 2 | which this module is dependent upon. This directory may be eliminated for a specific 3 | module if native binaries are not provided for the corresponding platform. 4 | -------------------------------------------------------------------------------- /os/osx64/README.txt: -------------------------------------------------------------------------------- 1 | The "os/osx64" directory is intended to hold macOS (OS X) native binaries 2 | which this module is dependent upon. This directory may be eliminated for a specific 3 | module if native binaries are not provided for the corresponding platform. 4 | -------------------------------------------------------------------------------- /os/win64/README.txt: -------------------------------------------------------------------------------- 1 | The "os/win64" directory is intended to hold MS Windows native binaries (.exe) 2 | which this module is dependent upon. This directory may be eliminated for a specific 3 | module if native binaries are not provided for the corresponding platform. 4 | -------------------------------------------------------------------------------- /src/main/java/dragondance/DragondancePlugin.java: -------------------------------------------------------------------------------- 1 | package dragondance; 2 | 3 | import dragondance.components.MainDockProvider; 4 | import dragondance.eng.DragonHelper; 5 | import dragondance.eng.Painter; 6 | import dragondance.eng.session.Session; 7 | import dragondance.eng.session.SessionManager; 8 | import dragondance.scripting.DragonDanceScripting; 9 | import ghidra.app.plugin.PluginCategoryNames; 10 | import ghidra.app.plugin.ProgramPlugin; 11 | import ghidra.framework.plugintool.*; 12 | import ghidra.framework.plugintool.util.PluginStatus; 13 | import ghidra.program.flatapi.FlatProgramAPI; 14 | import ghidra.program.model.listing.Program; 15 | 16 | 17 | //@formatter:off 18 | @PluginInfo( 19 | status = PluginStatus.STABLE, 20 | packageName = DragondancePluginPackage.NAME, 21 | category = PluginCategoryNames.MISC, 22 | shortDescription = "code coverage visualizer", 23 | description = "this plugin visualizes binary coverage data that are " + 24 | "collected by Dynamorio or Intel Pin binary instrumentation tools" 25 | ) 26 | //@formatter:on 27 | public class DragondancePlugin extends ProgramPlugin { 28 | FlatProgramAPI api; 29 | MainDockProvider mainDock; 30 | 31 | 32 | public DragondancePlugin(PluginTool tool) { 33 | super(tool, true, true); 34 | 35 | mainDock = new MainDockProvider(tool,getName()); 36 | 37 | } 38 | 39 | @Override 40 | public void init() { 41 | super.init(); 42 | } 43 | 44 | @Override 45 | public void programActivated(Program program) { 46 | super.programActivated(program); 47 | 48 | api = new FlatProgramAPI(this.currentProgram); 49 | 50 | DragonHelper.init(tool, api); 51 | 52 | if (Globals.EnableLogging) { 53 | Log.setEnable(true); 54 | 55 | if (Globals.EnableLoggingFileOutput) 56 | Log.enableFileLogging(true); 57 | 58 | if (Globals.EnableGhidraConsoleOutput) 59 | Log.enableGhidraConsoleLogging(true); 60 | 61 | if (Globals.DebugMode) 62 | Log.enableDebug(true); 63 | 64 | if (Globals.EnableStdoutLog) 65 | Log.enableStdoutLogging(true); 66 | 67 | } 68 | 69 | //Multi-session logic implemented but not usable from the GUI. 70 | //So create a single unnamed session to get things work right. 71 | Session.createNew("Unnamed session", DragonHelper.getProgramName()); 72 | 73 | SessionManager.getActiveSession().setPainter(new Painter()); 74 | 75 | } 76 | 77 | @Override 78 | public void programClosed(Program program) { 79 | 80 | DragonDanceScripting.discardScriptingSession(true); 81 | 82 | try { 83 | SessionManager.getActiveSession().close(); 84 | } catch (Exception e) { 85 | 86 | } 87 | 88 | //TODO: this is temporary. 89 | 90 | super.programClosed(program); 91 | 92 | } 93 | 94 | @Override 95 | public void dispose() { 96 | super.dispose(); 97 | 98 | Log.done(); 99 | 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /src/main/java/dragondance/DragondancePluginPackage.java: -------------------------------------------------------------------------------- 1 | package dragondance; 2 | 3 | import ghidra.framework.plugintool.util.PluginPackage; 4 | 5 | public class DragondancePluginPackage extends PluginPackage { 6 | 7 | public static final String NAME="dragondance"; 8 | 9 | public DragondancePluginPackage() { 10 | super(NAME, null, "dragondance plugin package"); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/main/java/dragondance/Globals.java: -------------------------------------------------------------------------------- 1 | package dragondance; 2 | 3 | public class Globals { 4 | public static boolean WithoutGhidra=false; 5 | public static boolean DebugMode=false; 6 | 7 | public static boolean EnableLogging=true; 8 | public static boolean EnableGhidraConsoleOutput=false; 9 | public static boolean EnableLoggingFileOutput=true; 10 | public static boolean EnableStdoutLog=false; 11 | public static boolean DumpInstructions=false; 12 | 13 | public static String LastFileDialogPath=""; 14 | 15 | public static final float MIN_HUE = 190.0f; 16 | public static final float MAX_HUE = 360.0f; 17 | 18 | public static final String version = "0.2.2"; 19 | public static final String verPhase = "beta"; 20 | 21 | } 22 | -------------------------------------------------------------------------------- /src/main/java/dragondance/Log.java: -------------------------------------------------------------------------------- 1 | package dragondance; 2 | 3 | import java.io.FileNotFoundException; 4 | import java.io.FileOutputStream; 5 | import java.io.PrintWriter; 6 | import java.time.LocalDateTime; 7 | 8 | import dragondance.eng.DragonHelper; 9 | 10 | public class Log { 11 | final static int LOGGER_ENABLED = 1 << 0; 12 | 13 | final static int LOGGER_LOG_GHIDRA_CONSOLE = 1 << 8; 14 | final static int LOGGER_LOG_FILE = 1 << 9; 15 | final static int LOGGER_LOG_STDOUT= 1 << 10; 16 | 17 | final static int LOGGER_ERROR = 1 << 22; 18 | final static int LOGGER_WARNING = 1 << 23; 19 | final static int LOGGER_INFO = 1 << 24; 20 | final static int LOGGER_DEBUG = 1 << 25; 21 | final static int LOGGER_VERBOSE = 1 << 26; 22 | 23 | private static int logFlag=0; 24 | private static String logFile = "dragondance.log"; 25 | 26 | private static FileOutputStream fos; 27 | private static PrintWriter pw; 28 | 29 | private static boolean createLogFile() { 30 | 31 | if (fos != null) 32 | return true; 33 | 34 | try { 35 | fos = new FileOutputStream(logFile,true); 36 | } catch (FileNotFoundException | SecurityException e) { 37 | return false; 38 | } 39 | 40 | pw = new PrintWriter(fos,true); 41 | 42 | pw.println("Logging started at " + LocalDateTime.now().toString()); 43 | 44 | return true; 45 | } 46 | 47 | private static void closeLogFile() { 48 | 49 | if (pw != null) 50 | { 51 | pw.close(); 52 | pw=null; 53 | fos=null; 54 | } 55 | } 56 | 57 | 58 | private static boolean flagSetReset(final int v, boolean enabled) { 59 | boolean previous = (logFlag & v) == v; 60 | 61 | if (enabled) 62 | logFlag |= v; 63 | else 64 | logFlag &= ~v; 65 | 66 | return previous; 67 | } 68 | 69 | private static final boolean hasFlag(final int flag) { 70 | return (logFlag & flag) == flag; 71 | } 72 | 73 | public static void setEnable(boolean enabled) { 74 | 75 | flagSetReset(LOGGER_ENABLED,enabled); 76 | 77 | if (!enabled && hasFlag(LOGGER_LOG_FILE)) 78 | enableFileLogging(false); 79 | } 80 | 81 | public static void enableGhidraConsoleLogging(boolean enabled) { 82 | flagSetReset(LOGGER_LOG_GHIDRA_CONSOLE,enabled); 83 | } 84 | 85 | public static void enableFileLogging(boolean enabled) { 86 | 87 | if (enabled) { 88 | if (!createLogFile()) 89 | return; 90 | } 91 | else 92 | closeLogFile(); 93 | 94 | flagSetReset(LOGGER_LOG_FILE,enabled); 95 | } 96 | 97 | public static boolean enableStdoutLogging(boolean enabled) { 98 | return flagSetReset(LOGGER_LOG_STDOUT,enabled); 99 | } 100 | 101 | public static boolean enableError(boolean enable) { 102 | return flagSetReset(LOGGER_ERROR,enable); 103 | } 104 | 105 | public static boolean enableWarning(boolean enable) { 106 | return flagSetReset(LOGGER_WARNING,enable); 107 | } 108 | 109 | public static boolean enableInfo(boolean enable) { 110 | return flagSetReset(LOGGER_INFO,enable); 111 | } 112 | 113 | public static boolean enableVerbose(boolean enable) { 114 | return flagSetReset(LOGGER_VERBOSE,enable); 115 | } 116 | 117 | public static boolean enableDebug(boolean enable) { 118 | return flagSetReset(LOGGER_DEBUG,enable); 119 | } 120 | 121 | private static void print(String logType, String format, Object... args) { 122 | String slog = ""; 123 | 124 | 125 | if (!hasFlag(LOGGER_ENABLED)) 126 | return; 127 | 128 | //emulate %p formatter in java. 129 | String pRepl = System.getProperty("os.arch"). 130 | contains("64") ? "0x%016x" : "0x%08x"; 131 | format = format.replace("%p", pRepl); 132 | 133 | if (logType != null && !logType.isEmpty()) { 134 | slog = "(" + logType + "): "; 135 | } 136 | 137 | slog += String.format(format, args); 138 | 139 | 140 | if (hasFlag(LOGGER_LOG_GHIDRA_CONSOLE)) 141 | DragonHelper.printConsole(slog); 142 | 143 | if (hasFlag(LOGGER_LOG_FILE)) { 144 | pw.print(slog); 145 | pw.flush(); 146 | } 147 | 148 | //Put the stdout (in debug mode this content printed into eclipse console) 149 | 150 | if (hasFlag(LOGGER_LOG_STDOUT)) 151 | System.out.print(slog); 152 | 153 | } 154 | 155 | public static void plain(String format, Object...args) { 156 | print("",format,args); 157 | } 158 | 159 | public static void println(String format, Object... args) { 160 | print("",format + "\n",args); 161 | } 162 | 163 | public static void println(String logType, String format, Object...args) { 164 | print(logType,format + "\n",args); 165 | } 166 | 167 | public static void error(String format, Object... args) { 168 | 169 | if (!hasFlag(LOGGER_ERROR)) 170 | return; 171 | 172 | println("Error",format,args); 173 | } 174 | 175 | 176 | public static void warning(String format, Object... args) { 177 | 178 | if (!hasFlag(LOGGER_WARNING)) 179 | return; 180 | 181 | println("Warning",format,args); 182 | } 183 | 184 | 185 | public static void info(String format, Object... args) { 186 | 187 | if (!hasFlag(LOGGER_INFO)) 188 | return; 189 | 190 | println("Info",format,args); 191 | } 192 | 193 | public static void verbose(String format, Object... args) { 194 | 195 | if (!hasFlag(LOGGER_VERBOSE)) 196 | return; 197 | 198 | println("Verbose",format,args); 199 | } 200 | 201 | public static void debug(String format, Object... args) { 202 | 203 | if (!hasFlag(LOGGER_DEBUG)) 204 | return; 205 | 206 | println("Dbg",format,args); 207 | } 208 | 209 | public static boolean isEnabled() { 210 | return hasFlag(LOGGER_ENABLED); 211 | } 212 | 213 | public static void done() { 214 | logFlag = 0; 215 | 216 | closeLogFile(); 217 | } 218 | 219 | } 220 | -------------------------------------------------------------------------------- /src/main/java/dragondance/StringResources.java: -------------------------------------------------------------------------------- 1 | package dragondance; 2 | 3 | public final class StringResources { 4 | private static final String nl = System.lineSeparator(); 5 | 6 | public static final String INVALID_CODE_ADDRESS_FIX_MESSAGE = "%x is valid executable code address but " + 7 | "ghidra can't recognize it as an address of an instruction. probably due to incorrectly analyze" + 8 | nl + nl + 9 | "but it can be fixed with disassembling this section. you can choose yes to fix, " + 10 | "or choose no to review yourself. (the address located)"; 11 | 12 | public static final String MISMATCHED_EXECUTABLE = "possible reasons:" + nl + nl + 13 | "1) the binary could be different version of the program" + nl + 14 | "the MD5 hash of the currently loaded image is \"%s\"" + nl + nl + 15 | "2) you forgot to reload the updated binary into ghidra." + nl + nl + 16 | "3) you tried to load a coverage data to the mismatched program"; 17 | 18 | 19 | public static final String ABOUT = "Dragon Dance (" + Globals.version + ")" + nl + nl + 20 | "by oguz kartal" + nl + 21 | "http://oguzkartal.net/"; 22 | 23 | public static final String NEW_VERSION = "There is new version of the dragondance." + nl + 24 | "You can get the latest version from the github repo's release section"; 25 | 26 | public static final String UP_TO_DATE = "It's up to date. cool!"; 27 | 28 | public static final String ATLEAST_2_COVERAGES = "You have to select at least 2 coverages to do this operation"; 29 | 30 | public static final String COVERAGE_IMPORTED_HINT = "Coverage data imported" + nl + 31 | "You can select list item" + nl + 32 | "to see coverage details"; 33 | } 34 | -------------------------------------------------------------------------------- /src/main/java/dragondance/components/GuiAffectedOpInterface.java: -------------------------------------------------------------------------------- 1 | package dragondance.components; 2 | 3 | import java.io.FileNotFoundException; 4 | 5 | import dragondance.datasource.CoverageData; 6 | 7 | public interface GuiAffectedOpInterface { 8 | public CoverageData loadCoverage(String coverageDataFile) throws FileNotFoundException; 9 | public boolean removeCoverage(int id); 10 | public boolean visualizeCoverage(CoverageData coverage); 11 | public boolean goTo(long offset); 12 | } 13 | -------------------------------------------------------------------------------- /src/main/java/dragondance/components/MainDockProvider.java: -------------------------------------------------------------------------------- 1 | package dragondance.components; 2 | 3 | import java.awt.Color; 4 | import java.awt.Font; 5 | import java.awt.Graphics; 6 | import java.awt.Rectangle; 7 | import java.awt.event.ComponentEvent; 8 | import java.awt.event.ComponentListener; 9 | import java.awt.event.InputEvent; 10 | import java.awt.event.KeyEvent; 11 | import java.awt.event.KeyListener; 12 | import java.io.FileNotFoundException; 13 | 14 | import javax.swing.*; 15 | import javax.swing.border.BevelBorder; 16 | import javax.swing.event.ListSelectionEvent; 17 | import javax.swing.event.ListSelectionListener; 18 | import javax.swing.table.DefaultTableModel; 19 | 20 | import docking.ActionContext; 21 | import docking.ComponentProvider; 22 | import docking.action.DockingAction; 23 | import docking.action.MenuData; 24 | import docking.action.ToolBarData; 25 | import docking.widgets.table.GTable; 26 | import dragondance.StringResources; 27 | import dragondance.datasource.CoverageData; 28 | import dragondance.datasource.CoverageDataSource; 29 | import dragondance.eng.DragonHelper; 30 | import dragondance.eng.session.Session; 31 | import dragondance.eng.session.SessionManager; 32 | import dragondance.exceptions.InvalidInstructionAddress; 33 | import dragondance.exceptions.OperationAbortedException; 34 | import dragondance.scripting.DragonDanceScripting; 35 | import dragondance.util.TextGraphic; 36 | import dragondance.util.Util; 37 | import ghidra.framework.plugintool.PluginTool; 38 | import resources.Icons; 39 | 40 | 41 | public class MainDockProvider extends ComponentProvider implements GuiAffectedOpInterface, ComponentListener { 42 | 43 | private JPanel panel = null; 44 | private JTextArea txtScript = null; 45 | private JScrollPane scriptTextScrollPane=null; 46 | private PluginTool tool = null; 47 | private GTable table = null; 48 | private DefaultTableModel dtm = null; 49 | private JLabel infoLabel = null; 50 | private TextGraphic txtGraph = null; 51 | private JScrollPane scrollPane = null; 52 | 53 | private boolean scriptShown=false; 54 | 55 | int infoPanelWidth=425,infoPanelHeight=75; 56 | int covTableWidth=425,covTableHeight=175; 57 | 58 | private int lastX=15,lastY=5; 59 | 60 | public MainDockProvider(PluginTool tool,String owner) { 61 | super(tool, "Dragon Dance", owner); 62 | 63 | DragonDanceScripting.setGuiAffectedInterface(this); 64 | 65 | this.tool = tool; 66 | 67 | createUi(); 68 | createActions(); 69 | 70 | this.getComponent().addComponentListener(this); 71 | 72 | } 73 | 74 | private Font getFont(int size, int type, String ...fontNames) { 75 | Font font; 76 | 77 | for (String fontName : fontNames) { 78 | font = new Font(fontName,type,size); 79 | 80 | if (font.getFamily() != null) 81 | return font; 82 | } 83 | 84 | return this.getComponent().getFont().deriveFont(type, size); 85 | } 86 | 87 | private void createActions() { 88 | DockingAction actShell = new DockingAction("shell",getName()) { 89 | 90 | @Override 91 | public void actionPerformed(ActionContext context) { 92 | ((MainDockProvider)context.getComponentProvider()).buildScriptingUi(); 93 | } 94 | }; 95 | 96 | 97 | 98 | 99 | DockingAction actAbout = new DockingAction("about",getName()) { 100 | 101 | @Override 102 | public void actionPerformed(ActionContext context) { 103 | DragonHelper.showMessage(StringResources.ABOUT); 104 | } 105 | 106 | }; 107 | 108 | DockingAction actCheckNewVer = new DockingAction("checkupdate",getName()) { 109 | @Override 110 | public void actionPerformed(ActionContext context) { 111 | if (Util.checkForNewVersion()) { 112 | DragonHelper.showMessage(StringResources.NEW_VERSION); 113 | } 114 | else { 115 | DragonHelper.showMessage(StringResources.UP_TO_DATE); 116 | } 117 | } 118 | }; 119 | 120 | actShell.setMenuBarData( 121 | new MenuData(new String[] { "Scripting shell" }, null, null)); 122 | 123 | actAbout.setMenuBarData( 124 | new MenuData(new String[] {"About"},null,null)); 125 | 126 | actCheckNewVer.setMenuBarData( 127 | new MenuData(new String[] {"Check for update"},null,null)); 128 | 129 | tool.addLocalAction(this, actShell); 130 | tool.addLocalAction(this, actAbout); 131 | tool.addLocalAction(this, actCheckNewVer); 132 | 133 | DockingAction actImport = new DockingAction("Import coverage data",getName()) { 134 | 135 | @Override 136 | public void actionPerformed(ActionContext context) { 137 | ((MainDockProvider)context.getComponentProvider()).importCoverageAsync(); 138 | } 139 | 140 | }; 141 | 142 | actImport.setToolBarData(new ToolBarData(Icons.ADD_ICON, null)); 143 | 144 | tool.addLocalAction(this, actImport); 145 | actImport.setEnabled(true); 146 | } 147 | 148 | private String newLine(int count) { 149 | String nl = ""; 150 | 151 | while (count-- > 0) { 152 | nl += System.lineSeparator(); 153 | } 154 | 155 | return nl; 156 | } 157 | 158 | private Session getSession() { 159 | Session session = SessionManager.getActiveSession(); 160 | 161 | if (session == null) { 162 | DragonHelper.showWarning("There is no active session"); 163 | return null; 164 | } 165 | 166 | return session; 167 | } 168 | 169 | private void blit() { 170 | this.infoLabel.repaint(); 171 | } 172 | 173 | private void writeStatusTextInfoPanel(String msg) { 174 | String[] lines = msg.split("\n"); 175 | 176 | for (String line : lines) { 177 | txtGraph.textOut(line, Color.BLACK).newLine(); 178 | } 179 | 180 | txtGraph.render(true); 181 | 182 | blit(); 183 | } 184 | 185 | private void setStatusText(String text) { 186 | if (DragonHelper.isUiDispatchThread()) 187 | this.getTool().setStatusInfo(text); 188 | else { 189 | DragonHelper.runOnSwingThread(() -> 190 | { 191 | getTool().setStatusInfo(text); 192 | }, 193 | true); 194 | } 195 | } 196 | 197 | private void drawSelectedCoverageDataInfo(int id) { 198 | 199 | Session session = getSession(); 200 | 201 | if (session == null) 202 | return; 203 | 204 | CoverageData cov = session.getCoverage(id); 205 | CoverageDataSource source = cov.getSource(); 206 | 207 | txtGraph.pushFont("Arial", 14, Font.PLAIN); 208 | 209 | txtGraph.textOut("Module count: ", Color.BLUE). 210 | textOut("%d", Color.BLACK,source.getModuleCount()).newLine(); 211 | 212 | txtGraph.textOut("Entry count: ", Color.BLUE). 213 | textOut("%d", Color.BLACK,source.getEntryCount()).newLine(); 214 | 215 | txtGraph.textOut("Readed module count: ", Color.BLUE). 216 | textOut("%d", Color.BLACK,source.getReadedModuleCount()).newLine(); 217 | 218 | txtGraph.textOut("Readed entry count: ", Color.BLUE). 219 | textOut("%d", Color.BLACK,source.getReadedEntryCount()).newLine(); 220 | 221 | txtGraph.floatTextBlock(); 222 | 223 | 224 | txtGraph.textOut("Initial range count: ", Color.RED). 225 | textOut("%d", Color.BLACK,cov.getInitialRangeCount()).newLine(); 226 | 227 | txtGraph.textOut("Range count: ", Color.RED). 228 | textOut("%d", Color.BLACK,cov.getRangeCount()).newLine(); 229 | 230 | txtGraph.textOut("Merged range count: ", Color.RED). 231 | textOut("%d", Color.BLACK,cov.getMergedRangeCount()).newLine(); 232 | 233 | txtGraph.textOut("Max density: ", Color.RED). 234 | textOut("%d", Color.BLACK,cov.getMaxDensity()).newLine(); 235 | 236 | txtGraph.render(true); 237 | 238 | blit(); 239 | 240 | } 241 | 242 | private void addCoverageTable(CoverageData coverage) { 243 | dtm.addRow(new Object[] { 244 | coverage.getSourceId(), 245 | coverage.getName(), 246 | Session.getCoverageTypeString(coverage.getSource().getType()) 247 | }); 248 | 249 | dtm.fireTableDataChanged(); 250 | } 251 | 252 | private void removeFromCoverageTable(int id) { 253 | int row = coverageIdToTableRow(id); 254 | 255 | dtm.removeRow(row); 256 | dtm.fireTableDataChanged(); 257 | } 258 | 259 | private CoverageData importCoverage(String coverageFile) throws FileNotFoundException { 260 | CoverageData coverage; 261 | Session session = getSession(); 262 | 263 | if (session == null) 264 | return null; 265 | 266 | coverage = session.addCoverageData(coverageFile); 267 | 268 | setStatusText("Loading..."); 269 | 270 | if (!coverage.getSource().process()) { 271 | DragonHelper.showWarning("%s could not be processed",coverageFile); 272 | return null; 273 | } 274 | 275 | try { 276 | if (coverage.build()) { 277 | 278 | Runnable postBuildGuiOp = new Runnable() { 279 | @Override 280 | public void run() { 281 | addCoverageTable(coverage); 282 | writeStatusTextInfoPanel(StringResources.COVERAGE_IMPORTED_HINT); 283 | setStatusText("Done"); 284 | } 285 | }; 286 | 287 | if (DragonHelper.isUiDispatchThread()) 288 | postBuildGuiOp.run(); 289 | else 290 | DragonHelper.runOnSwingThread(postBuildGuiOp, true); 291 | 292 | } 293 | } catch (InvalidInstructionAddress e1) { 294 | 295 | session.removeCoverageData(coverage.getSourceId()); 296 | 297 | String msg = e1.getMessage() + newLine(2) + StringResources.MISMATCHED_EXECUTABLE; 298 | DragonHelper.showWarning(msg, DragonHelper.getExecutableMD5Hash()); 299 | 300 | session.removeCoverageData(coverage.getSourceId()); 301 | 302 | return null; 303 | 304 | } catch (OperationAbortedException e1) { 305 | 306 | DragonHelper.showWarning( 307 | "Operation could not be continue. (" + 308 | e1.getMessage() + ")"); 309 | 310 | session.removeCoverageData(coverage.getSourceId()); 311 | 312 | return null; 313 | } 314 | 315 | 316 | return coverage; 317 | 318 | } 319 | 320 | 321 | private void importCoverageAsync() { 322 | String file = DragonHelper.askFile(tool.getToolFrame(),"Select coverage data", "load it up!"); 323 | 324 | if (file == null) 325 | return; 326 | 327 | DragonHelper.queuePoolWorkItem(() -> { 328 | try { 329 | importCoverage(file); 330 | } catch (FileNotFoundException e) { 331 | DragonHelper.showWarning("File not found"); 332 | } 333 | }); 334 | } 335 | 336 | private int coverageIdToTableRow(int id) { 337 | for (int i=0;i -1) 349 | { 350 | int id = ((Number)dtm.getValueAt(selIndex, 0)).intValue(); 351 | return id; 352 | } 353 | 354 | return 0; 355 | } 356 | 357 | private int[] getSelectedCoverageIds() { 358 | int [] selIndexes = table.getSelectedRows(); 359 | int [] ids = new int[selIndexes.length]; 360 | int i=0; 361 | 362 | if (selIndexes.length > 0) { 363 | for (int index : selIndexes) { 364 | ids[i++] = ((Number)dtm.getValueAt(index, 0)).intValue(); 365 | } 366 | } 367 | 368 | return ids; 369 | } 370 | 371 | private void onDeleteCoverageItemClick() { 372 | Session session = getSession(); 373 | 374 | if (session == null) 375 | return; 376 | 377 | int[] ids = getSelectedCoverageIds(); 378 | 379 | if (ids.length == 0) 380 | return; 381 | 382 | for (int id : ids) { 383 | if (session.removeCoverageData(id)) 384 | removeFromCoverageTable(id); 385 | } 386 | 387 | 388 | } 389 | 390 | private void onSwitchCoverageItemClick() { 391 | Session session = null; 392 | int id = getSelectedCoverageId(); 393 | 394 | session = getSession(); 395 | 396 | if (session == null) 397 | return; 398 | 399 | if (id > 0) { 400 | session.setActiveCoverage(id); 401 | } 402 | } 403 | 404 | private CoverageData[] getSelectedCoverageObjects() { 405 | Session session = getSession(); 406 | 407 | if (session == null) 408 | return null; 409 | 410 | int[] ids = getSelectedCoverageIds(); 411 | 412 | if (ids.length < 2) { 413 | DragonHelper.showWarning(StringResources.ATLEAST_2_COVERAGES); 414 | return null; 415 | } 416 | 417 | CoverageData[] coverages = new CoverageData[ids.length]; 418 | int i=0; 419 | 420 | for (int id : ids) { 421 | coverages[i++] = session.getCoverage(id); 422 | } 423 | 424 | return coverages; 425 | } 426 | 427 | private final static int OMT_INTERSECT=0; 428 | private final static int OMT_DIFF=1; 429 | private final static int OMT_DISTINCT=2; 430 | private final static int OMT_SUM=3; 431 | 432 | private void showMultiCoverageOperation(int type) { 433 | Session session = getSession(); 434 | CoverageData[] coverages = null; 435 | CoverageData result = null; 436 | 437 | if (session == null) { 438 | return; 439 | } 440 | 441 | coverages = getSelectedCoverageObjects(); 442 | 443 | switch (type) { 444 | case OMT_INTERSECT: 445 | result = CoverageData.intersect(coverages); 446 | break; 447 | case OMT_DIFF: 448 | result = CoverageData.difference(coverages); 449 | break; 450 | case OMT_DISTINCT: 451 | result = CoverageData.distinct(coverages); 452 | break; 453 | case OMT_SUM: 454 | result = CoverageData.sum(coverages); 455 | break; 456 | } 457 | 458 | if (result != null) { 459 | session.setActiveCoverage(result); 460 | } 461 | 462 | } 463 | 464 | 465 | private void buildInfoPanel() { 466 | 467 | this.txtGraph = new TextGraphic(infoPanelWidth,infoPanelHeight, panel.getBackground()); 468 | 469 | this.infoLabel = new JLabel() { 470 | @Override 471 | public void paintComponent(Graphics g) { 472 | txtGraph.dispatch(g); 473 | } 474 | }; 475 | 476 | 477 | this.infoLabel.setBorder(BorderFactory.createCompoundBorder()); 478 | this.infoLabel.setBounds(15, lastY + 10, infoPanelWidth,infoPanelHeight); 479 | panel.add(this.infoLabel); 480 | 481 | } 482 | 483 | 484 | private void buildScriptingUi() { 485 | 486 | 487 | if (this.txtScript == null) { 488 | 489 | this.lastX += 15; 490 | 491 | this.txtScript = new JTextArea(); 492 | this.txtScript.setBorder(BorderFactory.createBevelBorder(BevelBorder.LOWERED)); 493 | this.txtScript.setWrapStyleWord(true); 494 | 495 | this.scriptTextScrollPane = new JScrollPane(this.txtScript); 496 | 497 | this.scriptTextScrollPane.setBounds(this.lastX, 5, getComponent().getWidth() - this.lastX - 20 , this.covTableHeight); 498 | 499 | this.txtScript.addKeyListener(new KeyListener() { 500 | 501 | @Override 502 | public void keyTyped(KeyEvent e) { 503 | } 504 | 505 | @Override 506 | public void keyPressed(KeyEvent e) { 507 | if (e.getKeyCode() == KeyEvent.VK_ENTER && 508 | (e.getModifiersEx() & InputEvent.ALT_DOWN_MASK) == InputEvent.ALT_DOWN_MASK) { 509 | 510 | 511 | DragonDanceScripting.execute(txtScript.getText()); 512 | 513 | } 514 | } 515 | 516 | @Override 517 | public void keyReleased(KeyEvent e) { 518 | 519 | }}); 520 | 521 | 522 | Font fnt = null; 523 | 524 | fnt = getFont(14,Font.PLAIN,"Consolas","Courier New","Times New Roman"); 525 | 526 | this.txtScript.setFont(fnt); 527 | 528 | this.panel.add(this.scriptTextScrollPane); 529 | 530 | this.scriptTextScrollPane.setVisible(true); 531 | this.txtScript.setVisible(true); 532 | 533 | this.getComponent().repaint(); 534 | 535 | this.scriptShown=true; 536 | } 537 | else { 538 | this.scriptShown = !this.scriptShown; 539 | 540 | this.scriptTextScrollPane.setVisible(this.scriptShown); 541 | 542 | if (!this.scriptShown) 543 | DragonDanceScripting.discardScriptingSession(true); 544 | } 545 | } 546 | 547 | private void buildCoverageListView() { 548 | 549 | JPopupMenu contextMenu; 550 | JMenuItem miDelete,miSwitch; 551 | JMenuItem miShowIntersected,miShowDifferences,miShowDistinct,miShowSum; 552 | 553 | miDelete = new JMenuItem("Delete"); 554 | miSwitch = new JMenuItem("Switch to"); 555 | 556 | miShowIntersected = new JMenuItem("Intersection"); 557 | miShowDifferences = new JMenuItem("Difference"); 558 | miShowDistinct = new JMenuItem("Distinct"); 559 | miShowSum = new JMenuItem("Sum"); 560 | 561 | miDelete.addActionListener(e -> { 562 | onDeleteCoverageItemClick(); 563 | }); 564 | 565 | miSwitch.addActionListener(e -> { 566 | onSwitchCoverageItemClick(); 567 | }); 568 | 569 | miShowIntersected.addActionListener(e -> { 570 | showMultiCoverageOperation(OMT_INTERSECT); 571 | }); 572 | 573 | miShowDifferences.addActionListener(e -> { 574 | showMultiCoverageOperation(OMT_DIFF); 575 | }); 576 | 577 | miShowDistinct.addActionListener(e -> { 578 | showMultiCoverageOperation(OMT_DISTINCT); 579 | }); 580 | 581 | miShowSum.addActionListener(e -> { 582 | showMultiCoverageOperation(OMT_SUM); 583 | }); 584 | 585 | contextMenu = new JPopupMenu(); 586 | 587 | contextMenu.add(miDelete); 588 | contextMenu.add(miSwitch); 589 | contextMenu.add(new JSeparator()); 590 | contextMenu.add(miShowIntersected); 591 | contextMenu.add(miShowDifferences); 592 | contextMenu.add(miShowDistinct); 593 | contextMenu.add(miShowSum); 594 | 595 | 596 | 597 | 598 | dtm = new DefaultTableModel(); 599 | 600 | dtm.addColumn("Coverage Id"); 601 | dtm.addColumn("Name"); 602 | dtm.addColumn("Source type"); 603 | 604 | 605 | table = new GTable(dtm); 606 | table.setComponentPopupMenu(contextMenu); 607 | 608 | scrollPane = new JScrollPane(table); 609 | 610 | scrollPane.setBounds(15, lastY, this.covTableWidth, this.covTableHeight); 611 | table.setBounds(scrollPane.getBounds()); 612 | 613 | lastY += this.covTableHeight; 614 | 615 | table.getSelectionModel().setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION); 616 | 617 | table.getSelectionModel().addListSelectionListener(new ListSelectionListener() { 618 | 619 | @Override 620 | public void valueChanged(ListSelectionEvent e) { 621 | 622 | int id = getSelectedCoverageId(); 623 | 624 | if (id > 0) 625 | drawSelectedCoverageDataInfo(id); 626 | } 627 | 628 | }); 629 | 630 | panel.add(scrollPane); 631 | } 632 | 633 | 634 | private void createUi() { 635 | this.panel = new JPanel(); 636 | this.panel.setLayout(null); 637 | this.panel.setSize(700, 500); 638 | 639 | buildCoverageListView(); 640 | 641 | buildInfoPanel(); 642 | 643 | lastX += this.covTableWidth ; 644 | 645 | setVisible(true); 646 | 647 | } 648 | 649 | @Override 650 | public JComponent getComponent() { 651 | return this.panel; 652 | } 653 | 654 | @Override 655 | public void componentActivated() { 656 | 657 | } 658 | 659 | @Override 660 | public void componentHidden() { 661 | 662 | } 663 | 664 | @Override 665 | public void componentShown() { 666 | 667 | } 668 | 669 | //ComponentListener implementations 670 | 671 | @Override 672 | public void componentResized(ComponentEvent e) { 673 | if (this.scriptShown) { 674 | Rectangle oldbound; 675 | oldbound = this.scriptTextScrollPane.getBounds(); 676 | int scriptTextWidth = getComponent().getWidth() - oldbound.x; 677 | 678 | scriptTextWidth -= 20; 679 | 680 | oldbound.width = scriptTextWidth; 681 | 682 | this.scriptTextScrollPane.setBounds(oldbound); 683 | } 684 | } 685 | 686 | @Override 687 | public void componentMoved(ComponentEvent e) { 688 | 689 | } 690 | 691 | @Override 692 | public void componentShown(ComponentEvent e) { 693 | 694 | } 695 | 696 | @Override 697 | public void componentHidden(ComponentEvent e) { 698 | 699 | } 700 | 701 | //------------------------------------ 702 | //Gui related operation interface to the dragondance scripting 703 | @Override 704 | public CoverageData loadCoverage(String coverageDataFile) throws FileNotFoundException { 705 | return importCoverage(coverageDataFile); 706 | } 707 | 708 | @Override 709 | public boolean removeCoverage(int id) { 710 | 711 | Session session = getSession(); 712 | 713 | if (session == null) 714 | return false; 715 | 716 | if (session.removeCoverageData(id)) 717 | removeFromCoverageTable(id); 718 | else 719 | return false; 720 | 721 | return true; 722 | } 723 | 724 | @Override 725 | public boolean visualizeCoverage(CoverageData coverage) { 726 | 727 | Session session = getSession(); 728 | 729 | if (session == null) { 730 | return false; 731 | } 732 | 733 | if (coverage == null) { 734 | session.setActiveCoverage(null); 735 | return true; 736 | } 737 | 738 | if (coverage.getRangeCount() > 0) { 739 | return session.setActiveCoverage(coverage); 740 | } 741 | 742 | coverage.closeNothrow(); 743 | 744 | DragonHelper.showWarning("result coverage empty, so there is no data to show"); 745 | 746 | return false; 747 | } 748 | 749 | @Override 750 | public boolean goTo(long offset) { 751 | boolean success = DragonHelper.goToAddress(DragonHelper.getImageBase().getOffset() + offset); 752 | 753 | if (!success) { 754 | DragonHelper.showWarning("offset 0x%x is not valid",offset); 755 | } 756 | 757 | return success; 758 | } 759 | 760 | 761 | 762 | } 763 | -------------------------------------------------------------------------------- /src/main/java/dragondance/datasource/BlockEntry.java: -------------------------------------------------------------------------------- 1 | package dragondance.datasource; 2 | 3 | public class BlockEntry { 4 | private int offset; 5 | private int size; 6 | private int mid; 7 | private int instCount; 8 | 9 | public BlockEntry(int entryOffset, int entrySize, int moduleId, int instructionCount) { 10 | this.offset=entryOffset; 11 | this.size=entrySize; 12 | this.mid=moduleId; 13 | this.instCount=instructionCount; 14 | } 15 | 16 | public final int getOffset() { 17 | return this.offset; 18 | } 19 | 20 | public final int getSize() { 21 | return this.size; 22 | } 23 | 24 | public final int getModuleId() { 25 | return this.mid; 26 | } 27 | 28 | public final int getInstructionCount() { 29 | return this.instCount; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/main/java/dragondance/datasource/CommonDatabaseDataSource.java: -------------------------------------------------------------------------------- 1 | package dragondance.datasource; 2 | 3 | import java.io.FileNotFoundException; 4 | 5 | /* 6 | Database file format 7 | 8 | */ 9 | 10 | public class CommonDatabaseDataSource extends CoverageDataSource { 11 | 12 | public CommonDatabaseDataSource(String sourceFile) throws FileNotFoundException { 13 | super(sourceFile, null,-1); 14 | } 15 | 16 | 17 | 18 | } 19 | -------------------------------------------------------------------------------- /src/main/java/dragondance/datasource/CoverageData.java: -------------------------------------------------------------------------------- 1 | package dragondance.datasource; 2 | 3 | import java.util.ArrayList; 4 | import java.util.Comparator; 5 | import java.util.HashMap; 6 | import java.util.HashSet; 7 | import java.util.List; 8 | 9 | import dragondance.Globals; 10 | import dragondance.Log; 11 | import dragondance.eng.CodeRange; 12 | import dragondance.eng.DragonHelper; 13 | import dragondance.eng.InstructionInfo; 14 | import dragondance.eng.Painter; 15 | import dragondance.eng.session.Session; 16 | import dragondance.eng.session.SessionManager; 17 | import dragondance.exceptions.InvalidInstructionAddress; 18 | import dragondance.exceptions.OperationAbortedException; 19 | 20 | class CodeRangeComparator implements Comparator { 21 | 22 | @Override 23 | public int compare(CodeRange o1, CodeRange o2) { 24 | return (int)(o1.getRangeStart() - o2.getRangeStart()); 25 | } 26 | 27 | } 28 | 29 | public class CoverageData implements AutoCloseable { 30 | private static CodeRangeComparator rangeListComparator = new CodeRangeComparator(); 31 | 32 | private List rangeList = null; 33 | private HashMap> addressMap = null; 34 | 35 | private CoverageDataSource source = null; 36 | private int maxDensity=0; 37 | 38 | private int initialRangeCount=0; 39 | private int mergedRangeCount=0; 40 | 41 | private boolean visualized=false; 42 | private boolean sorted=false; 43 | private boolean inClose=false; 44 | 45 | private Session ownerSession=null; 46 | 47 | public CoverageData(CoverageDataSource source) { 48 | this.source = source; 49 | this.addressMap = new HashMap>(); 50 | 51 | this.ownerSession = SessionManager.getActiveSession(); 52 | } 53 | 54 | private static CoverageData newLogical() { 55 | CoverageData cov = new CoverageData(null); 56 | cov.rangeList = new ArrayList(); 57 | 58 | return cov; 59 | } 60 | 61 | public static CoverageData intersect(CoverageData covData1, CoverageData covData2) { 62 | 63 | CodeRange lastRange = null; 64 | CoverageData isectResult = CoverageData.newLogical(); 65 | 66 | for (Long key : covData1.addressMap.keySet()) { 67 | if (covData2.addressMap.containsKey(key)) { 68 | InstructionInfo inst = covData1.lookupAddressMapSingle(key); 69 | 70 | lastRange = isectResult.pushRangeListNoThrow(lastRange,inst.getAddr(),inst.getSize(),false); 71 | 72 | } 73 | } 74 | 75 | isectResult.merge(); 76 | 77 | return isectResult; 78 | } 79 | 80 | public static CoverageData difference(CoverageData covData1, CoverageData covData2) { 81 | CodeRange lastRange = null; 82 | CoverageData diffResult; 83 | InstructionInfo inst; 84 | 85 | HashSet rightKeyset = new HashSet(); 86 | 87 | rightKeyset.addAll(covData2.addressMap.keySet()); 88 | 89 | diffResult = CoverageData.newLogical(); 90 | 91 | for (Long key : covData1.addressMap.keySet()) { 92 | if (!covData2.addressMap.containsKey(key)) { 93 | inst = covData1.lookupAddressMapSingle(key); 94 | 95 | lastRange = diffResult.pushRangeListNoThrow(lastRange, inst.getAddr(), inst.getSize(), false); 96 | } 97 | } 98 | 99 | 100 | diffResult.merge(); 101 | 102 | return diffResult; 103 | } 104 | 105 | public static CoverageData distinct(CoverageData covData1, CoverageData covData2) { 106 | CodeRange lastRange = null; 107 | CoverageData distinctResult; 108 | InstructionInfo inst; 109 | 110 | HashSet rightKeyset = new HashSet(); 111 | 112 | rightKeyset.addAll(covData2.addressMap.keySet()); 113 | 114 | distinctResult = CoverageData.newLogical(); 115 | 116 | for (Long key : covData1.addressMap.keySet()) { 117 | 118 | if (!covData2.addressMap.containsKey(key)) { 119 | 120 | inst = covData1.lookupAddressMapSingle(key); 121 | 122 | lastRange = distinctResult.pushRangeListNoThrow(lastRange, inst.getAddr(), inst.getSize(), false); 123 | 124 | } 125 | else 126 | rightKeyset.remove(key); 127 | 128 | } 129 | 130 | for (Long key : rightKeyset) { 131 | inst = covData2.lookupAddressMapSingle(key); 132 | lastRange = distinctResult.pushRangeListNoThrow(lastRange, inst.getAddr(), inst.getSize(), false); 133 | } 134 | 135 | distinctResult.merge(); 136 | 137 | return distinctResult; 138 | } 139 | 140 | public static CoverageData sum(CoverageData covData1, CoverageData covData2) { 141 | CoverageData sumResult = CoverageData.newLogical(); 142 | CodeRange lastRange=null; 143 | InstructionInfo inst=null; 144 | 145 | 146 | 147 | for (List list : covData1.addressMap.values()) { 148 | inst = list.get(0); 149 | 150 | if (inst != null) 151 | lastRange = sumResult.pushRangeListNoThrow(lastRange, inst.getAddr(), inst.getSize(), false); 152 | } 153 | 154 | 155 | for (List list : covData2.addressMap.values()) { 156 | inst = list.get(0); 157 | 158 | if (inst != null) 159 | lastRange = sumResult.pushRangeListNoThrow(lastRange, inst.getAddr(), inst.getSize(), false); 160 | 161 | } 162 | 163 | sumResult.merge(); 164 | 165 | return sumResult; 166 | } 167 | 168 | public static CoverageData and(CoverageData ...covDataList) { 169 | return intersect(covDataList); 170 | } 171 | 172 | public static CoverageData or(CoverageData ...covDataList) { 173 | return sum(covDataList); 174 | } 175 | 176 | public static CoverageData xor(CoverageData ...covDataList) { 177 | return distinct(covDataList); 178 | } 179 | 180 | 181 | public static CoverageData intersect(CoverageData ...covDataList) { 182 | CoverageData result; 183 | 184 | if (covDataList.length < 2) 185 | return null; 186 | 187 | result = intersect(covDataList[0],covDataList[1]); 188 | 189 | for (int i=2;i= j) 267 | i--; 268 | 269 | if (j > 0) 270 | j--; 271 | 272 | } 273 | } 274 | } 275 | } 276 | } 277 | 278 | private InstructionInfo lookupAddressMapSingle(long addrKey) { 279 | 280 | List list = null; 281 | 282 | if (!this.addressMap.containsKey(addrKey)) 283 | return null; 284 | 285 | list = this.addressMap.get(addrKey); 286 | 287 | if (list.size()>0) 288 | return list.get(0); 289 | 290 | return null; 291 | } 292 | 293 | private CodeRange pushRangeListNoThrow(CodeRange codeRange, long addr, int size, boolean isSequence) { 294 | try { 295 | return pushRangeList(codeRange, addr, size,isSequence); 296 | } catch (InvalidInstructionAddress | OperationAbortedException e) { 297 | Log.println("pushRangeList (%s)", e.getMessage()); 298 | } 299 | 300 | return null; 301 | } 302 | 303 | private CodeRange pushRangeList(CodeRange codeRange, long addr, int size, boolean isSequence) throws InvalidInstructionAddress, OperationAbortedException { 304 | final boolean singleInstruction = !isSequence; 305 | 306 | if (this.rangeList.isEmpty()) { 307 | codeRange = new CodeRange(this,addr,size,this.addressMap,singleInstruction); 308 | this.rangeList.add(codeRange); 309 | } 310 | else { 311 | if (!codeRange.tryApply(addr, size,singleInstruction)) { 312 | codeRange = new CodeRange(this, addr, size,this.addressMap, singleInstruction); 313 | this.rangeList.add(codeRange); 314 | } 315 | } 316 | 317 | return codeRange; 318 | } 319 | 320 | private void buildRanges() throws InvalidInstructionAddress, OperationAbortedException { 321 | long imgBase,addr; 322 | CodeRange codeRange = null; 323 | 324 | this.rangeList = new ArrayList(); 325 | 326 | imgBase = DragonHelper.getImageBase().getOffset(); 327 | 328 | Log.info("Generating initial code ranges. Total block entry: %d",source.entries.size()); 329 | 330 | for (BlockEntry be : source.entries) { 331 | 332 | addr = imgBase + be.getOffset(); 333 | codeRange = pushRangeList(codeRange, addr,be.getSize(),true); 334 | } 335 | 336 | this.initialRangeCount = this.rangeList.size(); 337 | 338 | Log.info("%d ranges generated.", this.rangeList.size()); 339 | Log.info("trying to merge ranges"); 340 | 341 | merge(); 342 | 343 | Log.info("final code range size: %d, %d range merged", this.rangeList.size(),this.mergedRangeCount); 344 | 345 | } 346 | 347 | public boolean build() throws InvalidInstructionAddress, OperationAbortedException { 348 | 349 | if (this.rangeList != null) 350 | return true; 351 | 352 | if (this.source == null) 353 | return false; 354 | 355 | if (!this.source.isProcessed()) 356 | return false; 357 | 358 | this.buildRanges(); 359 | 360 | if (Globals.DumpInstructions) { 361 | boolean pv,pd; 362 | pv = Log.enableVerbose(true); 363 | pd = Log.enableDebug(true); 364 | this.dump(); 365 | this.dumpHashMap(); 366 | Log.enableVerbose(pv); 367 | Log.enableDebug(pd); 368 | } 369 | 370 | return true; 371 | } 372 | 373 | public void dump() { 374 | for (CodeRange range : this.rangeList) { 375 | range.dumpInstructionDensityList(); 376 | } 377 | } 378 | 379 | public void dumpHashMap() { 380 | List listValue; 381 | 382 | for (Long key : this.addressMap.keySet()) 383 | { 384 | listValue = this.addressMap.get(key); 385 | 386 | Log.debug("Key: %x | ",key); 387 | Log.debug("Value(s): "); 388 | 389 | for (InstructionInfo ii : listValue) { 390 | if (ii.getOwnerCodeRange().getContainerCoverage() == this) { 391 | Log.debug("%x (density: %d)," , ii.hashCode(), ii.getDensity()); 392 | } 393 | } 394 | 395 | Log.println(""); 396 | } 397 | } 398 | 399 | public void paint(Painter painter) { 400 | 401 | if (this.visualized) 402 | return; 403 | 404 | boolean failed=false; 405 | 406 | int transId = DragonHelper.startTransaction("BgPaint"); 407 | 408 | for (CodeRange range : this.rangeList) { 409 | if (!range.paintRange(painter)) { 410 | failed=true; 411 | break; 412 | } 413 | } 414 | 415 | DragonHelper.finishTransaction(transId,!failed); 416 | 417 | if (!failed) 418 | this.visualized=true; 419 | } 420 | 421 | public void clearPaint() { 422 | 423 | if (!this.visualized) 424 | return; 425 | 426 | int transId = DragonHelper.startTransaction("ClearBgPaint"); 427 | 428 | for (CodeRange range : this.rangeList) { 429 | range.clearPaint(); 430 | } 431 | 432 | DragonHelper.finishTransaction(transId, true); 433 | 434 | this.visualized=false; 435 | } 436 | 437 | public CoverageDataSource getSource() { 438 | return this.source; 439 | } 440 | 441 | public int getSourceId() { 442 | 443 | if (this.source==null) 444 | return 0; 445 | 446 | return this.source.getId(); 447 | } 448 | 449 | public void setMaxDensity(int density) { 450 | if (density > this.maxDensity) { 451 | Log.debug("new max density: %d", density); 452 | this.maxDensity = density; 453 | } 454 | } 455 | 456 | public final int getMaxDensity() { 457 | return this.maxDensity; 458 | } 459 | 460 | @Override 461 | public void close() throws Exception { 462 | 463 | if (this.inClose) 464 | return; 465 | 466 | this.inClose = true; 467 | 468 | if (this.ownerSession.isActiveCoverage(this)) { 469 | this.ownerSession.setActiveCoverage(null); 470 | } 471 | 472 | this.clearPaint(); 473 | 474 | if (this.rangeList != null) 475 | this.rangeList.clear(); 476 | 477 | if (this.addressMap != null) 478 | this.addressMap.clear(); 479 | 480 | if (!isLogicalCoverageData()) 481 | this.source.close(); 482 | 483 | this.ownerSession=null; 484 | 485 | this.inClose = false; 486 | 487 | } 488 | 489 | public void closeNothrow() { 490 | try { 491 | close(); 492 | } catch (Exception e) { 493 | } 494 | } 495 | 496 | public final int getRangeCount() { 497 | 498 | if (this.rangeList==null) 499 | return 0; 500 | 501 | return this.rangeList.size(); 502 | } 503 | 504 | public final int getInitialRangeCount() { 505 | return this.initialRangeCount; 506 | } 507 | 508 | public final int getMergedRangeCount() { 509 | return this.mergedRangeCount; 510 | } 511 | 512 | public void sort() { 513 | if (!this.sorted) { 514 | this.rangeList.sort(rangeListComparator); 515 | this.sorted=true; 516 | } 517 | } 518 | 519 | public final String getName() { 520 | if (this.source == null) { 521 | return ""; 522 | } 523 | 524 | return this.source.getName(); 525 | } 526 | 527 | public final String getSourceFilePath() { 528 | if (this.source == null) { 529 | return ""; 530 | } 531 | 532 | return this.source.getFilePath(); 533 | } 534 | 535 | public final boolean isLogicalCoverageData() { 536 | return this.source == null; 537 | } 538 | } 539 | -------------------------------------------------------------------------------- /src/main/java/dragondance/datasource/CoverageDataSource.java: -------------------------------------------------------------------------------- 1 | package dragondance.datasource; 2 | 3 | import java.io.File; 4 | import java.io.FileInputStream; 5 | import java.io.FileNotFoundException; 6 | import java.io.IOException; 7 | import java.nio.ByteBuffer; 8 | import java.util.ArrayList; 9 | import java.util.List; 10 | 11 | import dragondance.Log; 12 | import dragondance.util.Util; 13 | 14 | enum ByteMapTypes 15 | { 16 | Uint32, 17 | UShort 18 | } 19 | 20 | public class CoverageDataSource implements AutoCloseable{ 21 | 22 | public static final int SOURCE_TYPE_DYNA = 0; 23 | public static final int SOURCE_TYPE_PINTOOL = 1; 24 | 25 | 26 | protected int moduleCount=0; 27 | protected int entryTableSize=0; 28 | 29 | protected List modules; 30 | protected List entries; 31 | 32 | protected String mainModuleName = null; 33 | protected ModuleInfo mainModule = null; 34 | 35 | protected boolean isEof = false; 36 | protected boolean processed = false; 37 | private FileInputStream fis = null; 38 | private ByteBuffer buf = null; 39 | private String filePath; 40 | private int id = 0; 41 | private int type=-1; 42 | private String name; 43 | 44 | public CoverageDataSource(String sourceFile, String mainModule,int type) throws FileNotFoundException { 45 | this.mainModuleName=mainModule; 46 | this.type=type; 47 | 48 | File file = new File(sourceFile); 49 | 50 | if (!file.exists()) 51 | throw new FileNotFoundException(sourceFile); 52 | 53 | this.filePath = sourceFile; 54 | this.name = Util.getObjectNameFromPath(sourceFile); 55 | 56 | this.fis = new FileInputStream(file); 57 | this.buf = ByteBuffer.allocate(1 * 1024 * 1024); 58 | 59 | this.modules = new ArrayList(); 60 | this.entries = new ArrayList(); 61 | 62 | readIntoBuffer(); 63 | 64 | } 65 | 66 | public static int detectCoverageDataFileType(String file) throws FileNotFoundException { 67 | CoverageDataSource cds; 68 | int type = -1; 69 | 70 | cds = new CoverageDataSource(file,null,-1); 71 | 72 | String s = cds.readLine(); 73 | 74 | if (s.startsWith("DDPH-PINTOOL")) 75 | type = SOURCE_TYPE_PINTOOL; 76 | else if (s.startsWith("DRCOV VERSION:")) 77 | type = SOURCE_TYPE_DYNA; 78 | 79 | 80 | try { 81 | cds.close(); 82 | } catch (Exception e) { } 83 | 84 | return type; 85 | } 86 | 87 | private boolean readIntoBuffer() { 88 | 89 | this.buf.clear(); 90 | int readLen=0; 91 | 92 | if (this.isEof) 93 | return false; 94 | 95 | try { 96 | readLen = this.fis.read(this.buf.array(), 0, this.buf.capacity()); 97 | 98 | if (readLen == -1) { 99 | this.isEof=true; 100 | this.fis.close(); 101 | return false; 102 | } 103 | 104 | this.buf.limit(readLen); 105 | 106 | } catch (IOException e) { 107 | Log.println(e.getMessage()); 108 | return false; 109 | } 110 | 111 | return true; 112 | } 113 | 114 | private byte readByte() { 115 | byte b; 116 | 117 | if (this.buf.hasRemaining()) 118 | b = this.buf.get(); 119 | else { 120 | 121 | if (!readIntoBuffer()) 122 | return -1; 123 | 124 | b = this.buf.get(); 125 | } 126 | 127 | return b; 128 | } 129 | 130 | private int readBytes(byte[] rbuf, int bufOffset, int len) { 131 | 132 | int readLen=0; 133 | 134 | if (!this.buf.hasRemaining()) { 135 | if (!readIntoBuffer()) 136 | return -1; 137 | } 138 | 139 | if (this.buf.remaining() < len) { 140 | int rem=this.buf.remaining(); 141 | this.buf.get(rbuf, bufOffset, rem); 142 | 143 | readLen = rem; 144 | 145 | if (!readIntoBuffer()) { 146 | return readLen; 147 | } 148 | 149 | readLen += readBytes(rbuf,readLen,len-rem); 150 | 151 | } 152 | else { 153 | this.buf.get(rbuf,bufOffset,len); 154 | readLen = len; 155 | } 156 | 157 | 158 | return readLen; 159 | } 160 | 161 | protected String[] splitMultiDelim(String str, String delims, boolean trimItem) { 162 | int p=0,slen=str.length(); 163 | String s; 164 | 165 | if (slen == 0) 166 | return null; 167 | 168 | ArrayList parts = new ArrayList(); 169 | 170 | for (int i=0;i map = 51 | new HashMap(); 52 | 53 | //Map columns name into the index. 54 | //So drcov file version may vary on different format version. 55 | //That is generic solution for all 56 | for (int i=0;i -1; 48 | } 49 | 50 | public int getContainingId() { 51 | return c_id; 52 | } 53 | 54 | public int getId() { 55 | return id; 56 | } 57 | 58 | public long getBase() { 59 | return base; 60 | } 61 | 62 | public long getEnd() { 63 | return end; 64 | } 65 | 66 | public String getPath() { 67 | return path; 68 | } 69 | 70 | } 71 | -------------------------------------------------------------------------------- /src/main/java/dragondance/datasource/PintoolDataSource.java: -------------------------------------------------------------------------------- 1 | package dragondance.datasource; 2 | 3 | import java.io.FileNotFoundException; 4 | 5 | import dragondance.Log; 6 | 7 | public class PintoolDataSource extends CoverageDataSource { 8 | 9 | public PintoolDataSource(String sourceFile, String mainModule) throws FileNotFoundException { 10 | super(sourceFile, mainModule,CoverageDataSource.SOURCE_TYPE_PINTOOL); 11 | } 12 | 13 | private void parseInformation() { 14 | String line; 15 | String[] parts; 16 | 17 | while ((line = readLine()) != null) { 18 | if (line.startsWith("EntryCount:")) { 19 | parts = splitMultiDelim(line,":, ",false); 20 | this.entryTableSize = Integer.parseInt(parts[1]); 21 | this.moduleCount = Integer.parseInt(parts[3]); 22 | } 23 | else if (line.startsWith("MODULE_TABLE")) { 24 | readModules(); 25 | } 26 | else if (line.startsWith("ENTRY_TABLE")) { 27 | readEntries(); 28 | break; 29 | } 30 | } 31 | } 32 | 33 | private void readModules() { 34 | String line; 35 | int readModuleCount=0; 36 | 37 | while ((line = readLine()) != null) { 38 | String[] parts = splitMultiDelim(line,",", true); 39 | 40 | ModuleInfo mod = ModuleInfo.make( 41 | parts[0], 42 | parts[1], 43 | parts[2], 44 | parts[3] ); 45 | 46 | if (mod == null) { 47 | Log.warning("Module not parsed for: %s", line); 48 | continue; 49 | } 50 | 51 | this.pushModule(mod); 52 | 53 | if (++readModuleCount == this.moduleCount) 54 | break; 55 | } 56 | } 57 | 58 | private void readEntries() { 59 | BlockEntry entry; 60 | 61 | while ((entry = this.readEntryExtended()) != null) { 62 | this.pushEntry(entry); 63 | } 64 | } 65 | 66 | @Override 67 | public boolean process() { 68 | parseInformation(); 69 | this.processed=true; 70 | 71 | return super.process(); 72 | } 73 | 74 | } 75 | -------------------------------------------------------------------------------- /src/main/java/dragondance/eng/CodeRange.java: -------------------------------------------------------------------------------- 1 | package dragondance.eng; 2 | 3 | import java.util.ArrayList; 4 | import java.util.HashMap; 5 | import java.util.List; 6 | 7 | import dragondance.Globals; 8 | import dragondance.Log; 9 | import dragondance.datasource.CoverageData; 10 | import dragondance.exceptions.InvalidInstructionAddress; 11 | import dragondance.exceptions.OperationAbortedException; 12 | 13 | public class CodeRange implements AutoCloseable { 14 | 15 | private static int gs_RangeIndex=1; 16 | 17 | private long rangeStart,rangeEnd,rangeSize; 18 | 19 | private long avgInstSize=0; 20 | private long totalInstSize=0; 21 | private List densityList; 22 | private HashMap> map; 23 | 24 | private String name; 25 | private CoverageData container; 26 | 27 | private class IntersectionBound 28 | { 29 | public long begin,end; 30 | 31 | public final boolean areEqual() { 32 | return begin==end; 33 | } 34 | } 35 | 36 | public CodeRange(CoverageData container, long start, int size, HashMap> addressMap, boolean singleInstruction) throws InvalidInstructionAddress, OperationAbortedException { 37 | this.rangeStart = start; 38 | this.rangeEnd = start + size; 39 | this.rangeSize = size; 40 | this.container = container; 41 | this.map = addressMap; 42 | 43 | this.densityList = new ArrayList(); 44 | 45 | this.name = "Range " + String.valueOf(gs_RangeIndex); 46 | 47 | CodeRange.gs_RangeIndex++; 48 | 49 | this.add(start, size,singleInstruction); 50 | } 51 | 52 | public CodeRange(CoverageData container, long start, int size, boolean singleInstruction) throws InvalidInstructionAddress, OperationAbortedException { 53 | this(container,start,size,null,singleInstruction); 54 | } 55 | 56 | public CodeRange(CoverageData container, long start, int size) throws InvalidInstructionAddress, OperationAbortedException { 57 | this(container,start,size,false); 58 | } 59 | 60 | private void printRangeInfo() { 61 | Log.debug("Range start: %p, Range end: %p, size: %d", 62 | this.rangeStart,this.rangeEnd,this.rangeSize); 63 | 64 | } 65 | 66 | public void dumpInstructionDensityList() { 67 | for (InstructionInfo ii : this.densityList) 68 | { 69 | Log.verbose("addr: %p, size: %d, density: %d", ii.getAddr(),ii.getSize(),ii.getDensity()); 70 | } 71 | } 72 | 73 | private boolean copyList(List dest, CodeRange destRange, List src, int destCopyIndex, int srcCopyIndex, int size) { 74 | 75 | int dindex = destCopyIndex < 0 ? dest.size() : destCopyIndex; 76 | int sindex = srcCopyIndex < 0 ? src.size() : srcCopyIndex; 77 | 78 | if (dindex > dest.size()) 79 | dindex = dest.size(); 80 | 81 | if (sindex + size > src.size()) 82 | return false; 83 | 84 | 85 | while (size-- > 0) 86 | { 87 | InstructionInfo inst = InstructionInfo.cloneFor(src.get(sindex), destRange); 88 | 89 | dest.add(dindex, inst); 90 | this.putMapIfAvailable(inst); 91 | 92 | dindex++; 93 | sindex++; 94 | } 95 | 96 | return true; 97 | } 98 | 99 | private final boolean isInRange(long addr) { 100 | return addr >= this.rangeStart && addr <= this.rangeEnd; 101 | } 102 | 103 | private IntersectionBound getIntersectionBound(CodeRange source) { 104 | 105 | IntersectionBound isectBound=new IntersectionBound(); 106 | 107 | isectBound.begin = source.rangeStart > this.rangeStart ? 108 | source.rangeStart : this.rangeStart; 109 | 110 | isectBound.end = source.rangeEnd < this.rangeEnd ? 111 | source.rangeEnd : this.rangeEnd; 112 | 113 | return isectBound; 114 | } 115 | 116 | private void updateAvgInstSize(int size) { 117 | this.totalInstSize += size; 118 | this.avgInstSize = this.totalInstSize / this.densityList.size(); 119 | } 120 | 121 | private int getInstructionSize(long addr) throws InvalidInstructionAddress, OperationAbortedException { 122 | InstructionContext ictx = null; 123 | 124 | if (Globals.WithoutGhidra) 125 | return 4; 126 | 127 | ictx = DragonHelper.getInstruction(addr,true); 128 | 129 | if (ictx == null) { 130 | throw new OperationAbortedException(String.format("There is no valid instruction at %x",addr)); 131 | } 132 | 133 | return ictx.getSize(); 134 | } 135 | 136 | private int getIndexFromAddr(long addr) { 137 | 138 | InstructionInfo instInfo; 139 | 140 | int stepLimit=15,ri; 141 | 142 | if (!isInRange(addr)) 143 | return -1; 144 | 145 | if (this.densityList.isEmpty()) 146 | return -1; 147 | 148 | assert(this.avgInstSize != 0); 149 | 150 | //guess the index. most of the time our guess will hit for first time 151 | ri = (int)((addr - this.rangeStart) / this.avgInstSize); 152 | 153 | //reduce the guess if its out of the list 154 | if (ri >= this.densityList.size()) 155 | ri = this.densityList.size()/2; 156 | 157 | instInfo = this.densityList.get(ri); 158 | 159 | //test addresses 160 | if (instInfo.getAddr() == addr) 161 | return ri; 162 | 163 | if (instInfo.getAddr() > addr) 164 | { 165 | /* 166 | * Guessed address of the index greater than we looking for 167 | * So we have step back few times. 168 | * (That would not be nothing more than 15 times. 169 | * cuz of the x86's maximum instruction size) 170 | * 171 | * That would not be issue on the RISC instruction sets. 172 | */ 173 | 174 | while (ri > 0 && stepLimit > 0) 175 | { 176 | ri--; 177 | 178 | instInfo = this.densityList.get(ri); 179 | 180 | if (instInfo.getAddr() == addr) 181 | return ri; 182 | 183 | stepLimit--; 184 | } 185 | } 186 | else 187 | { 188 | //in this case we have step forward 189 | 190 | while (ri != this.densityList.size()-1 && stepLimit > 0) 191 | { 192 | ri++; 193 | 194 | instInfo = this.densityList.get(ri); 195 | 196 | if (instInfo.getAddr() == addr) 197 | return ri; 198 | 199 | stepLimit--; 200 | 201 | } 202 | 203 | } 204 | 205 | return -1; 206 | } 207 | 208 | private boolean incrementRangeDensity(long addr, int size) { 209 | int ri = getIndexFromAddr(addr); 210 | 211 | if (ri == -1) 212 | return false; 213 | 214 | while (size > 0) 215 | { 216 | InstructionInfo ictx = this.densityList.get(ri); 217 | 218 | ictx.incrementDensity(); 219 | size -= ictx.getSize(); 220 | 221 | ri++; 222 | } 223 | 224 | return true; 225 | } 226 | 227 | private boolean mergeFromInternal(CodeRange source) { 228 | 229 | int ri,sri,tmp; 230 | 231 | IntersectionBound ibound = getIntersectionBound(source); 232 | 233 | Log.debug("--------src range--------"); 234 | source.printRangeInfo(); 235 | Log.debug("--------self range--------"); 236 | this.printRangeInfo(); 237 | 238 | Log.debug("intersection start: %p, end: %p",ibound.begin,ibound.end); 239 | 240 | if (ibound.areEqual()) 241 | { 242 | Log.verbose("intersection points are same"); 243 | } 244 | 245 | //make sure the intersection start point is valid for the source range 246 | ri = source.getIndexFromAddr(ibound.begin); 247 | 248 | if (ri == -1) 249 | { 250 | if (ibound.areEqual()) 251 | ri = source.densityList.size(); 252 | else 253 | return false; //no they are not really intersected. 254 | } 255 | 256 | tmp = ri; 257 | 258 | sri = this.getIndexFromAddr(ibound.begin); 259 | 260 | if (sri == -1) 261 | { 262 | if (!ibound.areEqual()) 263 | return false; // they are not really intersected either. 264 | } 265 | 266 | if (!ibound.areEqual()) 267 | { 268 | //merge intersected range densities first. 269 | 270 | assert(this.densityList.get(sri).getAddr() == ibound.begin); 271 | 272 | while (sri != this.densityList.size() && 273 | this.densityList.get(sri).getAddr() < ibound.end) 274 | { 275 | this.densityList.get(sri).incrementDensityBy( 276 | source.densityList.get(ri).getDensity()); 277 | 278 | sri++; 279 | ri++; 280 | } 281 | } 282 | else 283 | { 284 | //intersection areas are same. there is no item to merge 285 | } 286 | 287 | //prepend lower address range part if exists 288 | if (source.rangeStart < this.rangeStart) 289 | { 290 | ri = tmp; 291 | copyList(this.densityList,this, source.densityList,0,0,ri); 292 | 293 | tmp = (int)this.rangeSize; 294 | 295 | this.rangeStart = source.rangeStart; 296 | this.rangeSize = this.rangeEnd - this.rangeStart; 297 | 298 | tmp = (int)this.rangeSize - tmp; 299 | 300 | updateAvgInstSize(tmp); 301 | 302 | } 303 | 304 | //append higher address range part if exists 305 | if (source.rangeEnd > this.rangeEnd) 306 | { 307 | sri = source.getIndexFromAddr(ibound.end); 308 | copyList(this.densityList,this, source.densityList,-1,sri,source.densityList.size()-sri); 309 | tmp = (int)this.rangeSize; 310 | 311 | this.rangeEnd = source.rangeEnd; 312 | this.rangeSize = this.rangeEnd - this.rangeStart; 313 | 314 | tmp = (int)this.rangeSize - tmp; 315 | updateAvgInstSize(tmp); 316 | } 317 | 318 | Log.debug("new range info"); 319 | this.printRangeInfo(); 320 | 321 | return true; 322 | } 323 | 324 | private void putMapIfAvailable(InstructionInfo inst) { 325 | List instList; 326 | 327 | if (this.map == null) 328 | return; 329 | 330 | if (this.map.containsKey(inst.getAddr())) { 331 | instList = this.map.get(inst.getAddr()); 332 | 333 | if (!instList.contains(inst)) { 334 | instList.add(inst); 335 | } 336 | else { 337 | Log.warning("%p already exists",inst.getAddr()); 338 | } 339 | 340 | } 341 | else { 342 | instList = new ArrayList(); 343 | instList.add(inst); 344 | 345 | this.map.put(inst.getAddr(), instList); 346 | } 347 | } 348 | 349 | private void removeMapIfAvailable(InstructionInfo inst) { 350 | List instList; 351 | 352 | if (this.map == null) 353 | return; 354 | 355 | Log.debug("Removing for inst: %x", inst.getAddr()); 356 | 357 | if (this.map.containsKey(inst.getAddr())) { 358 | instList = this.map.get(inst.getAddr()); 359 | 360 | for (int i=0;i eaddr) 407 | { 408 | /* 409 | * #hmm. the last instruction has overflowed the expected range size 410 | * so the last instruction must be discarded from the list. 411 | * this is violates our strict range bound 412 | */ 413 | 414 | InstructionInfo lastInst; 415 | 416 | lastInst = this.densityList.remove(this.densityList.size()-1); 417 | this.removeMapIfAvailable(lastInst); 418 | 419 | this.rangeEnd -= lastInst.getSize(); 420 | this.rangeSize = this.rangeEnd - this.rangeStart; 421 | 422 | } 423 | 424 | return true; 425 | } 426 | 427 | private boolean tryApplyOverlappedFromHead(long addr, int size) throws InvalidInstructionAddress, OperationAbortedException { 428 | List headPartList = new ArrayList(); 429 | InstructionInfo inst; 430 | 431 | int instSize=0; 432 | long currAddr = addr; 433 | 434 | while (true) 435 | { 436 | instSize = getInstructionSize(currAddr); 437 | 438 | if (instSize == 0) 439 | { 440 | headPartList.clear(); 441 | return false; 442 | } 443 | 444 | inst = new InstructionInfo(this,currAddr,instSize,1); 445 | 446 | headPartList.add(inst); 447 | 448 | currAddr += instSize; 449 | 450 | //stop if we are at the this range's start point 451 | if (currAddr == this.rangeStart) 452 | break; 453 | else if (currAddr > this.rangeEnd) 454 | { 455 | //hmm we are working on wrong place. so cancel operation 456 | headPartList.clear(); 457 | return false; 458 | } 459 | } 460 | 461 | //prepend head part to the current density list 462 | 463 | int nx=0; 464 | 465 | for (InstructionInfo ii : headPartList) { 466 | this.putMapIfAvailable(ii); 467 | this.densityList.add(nx++, ii); 468 | } 469 | 470 | //this.densityList.addAll(0, headPartList); 471 | headPartList.clear(); 472 | 473 | Log.debug("old range (start: %p, size: %d)", this.rangeStart,this.rangeSize); 474 | 475 | //set new range bound 476 | 477 | this.rangeStart = addr; 478 | this.rangeSize = this.rangeEnd - this.rangeStart; 479 | 480 | Log.debug("new range (start: %p, size: %d)", this.rangeStart,this.rangeSize); 481 | 482 | 483 | int remainSize = size - (int)(currAddr - addr); 484 | 485 | if (remainSize > 0) 486 | { 487 | //just increase the density of the intersected part. 488 | incrementRangeDensity(currAddr, remainSize); 489 | //TODO: beware its status 490 | } 491 | 492 | return true; 493 | } 494 | 495 | public boolean tryApply(long addr, int size, boolean singleInstruction) throws InvalidInstructionAddress, OperationAbortedException { 496 | 497 | if (!isInRange(addr)) 498 | { 499 | if (isInRange(addr+size)) 500 | { 501 | return tryApplyOverlappedFromHead(addr,size); 502 | } 503 | 504 | return false; 505 | } 506 | 507 | 508 | if (addr + size > this.rangeEnd) 509 | { 510 | //addr + size goes out of the range. but is the starting address 511 | //mapped in the range? 512 | int ri = getIndexFromAddr(addr); 513 | 514 | if (ri == -1) // the address wasn't mapped in the range. 515 | { 516 | //so is it at range's upper bound? 517 | if (this.rangeEnd == addr) 518 | { 519 | //yep. we can append the block 520 | return add(addr, size, singleInstruction); 521 | } 522 | 523 | //otherwise the address is completely invalid. 524 | return false; 525 | } 526 | 527 | //ok. first we have to increment density of the intersected range part 528 | incrementRangeDensity(addr,(int)(this.rangeEnd-addr)); 529 | 530 | //then append remaining part 531 | int excess = (int)((addr + size) - this.rangeEnd); 532 | 533 | Log.verbose("%d bytes exceeded. expanding range",excess); 534 | 535 | add(this.rangeEnd,excess,singleInstruction); 536 | } 537 | else //otherwise its completely overlapped. 538 | incrementRangeDensity(addr,size); 539 | 540 | return true; 541 | } 542 | 543 | public boolean tryApply(long addr, int size) throws InvalidInstructionAddress, OperationAbortedException { 544 | return tryApply(addr,size,false); 545 | } 546 | 547 | public boolean tryPutInstruction(long instAddr, int size) throws InvalidInstructionAddress, OperationAbortedException { 548 | return tryApply(instAddr,size,true); 549 | } 550 | 551 | public boolean mergeFrom(CodeRange sourceRange) { 552 | 553 | boolean canBeMerge=false; 554 | 555 | if (sourceRange.rangeStart <= this.rangeStart && 556 | sourceRange.rangeEnd >= this.rangeStart) 557 | { 558 | canBeMerge=true; 559 | } 560 | else if (sourceRange.rangeStart >= this.rangeStart && 561 | sourceRange.rangeStart <= this.rangeEnd) 562 | { 563 | canBeMerge=true; 564 | } 565 | 566 | if (canBeMerge != intersectable(sourceRange)) { 567 | Log.info("WARNING! canBeMerge=%s, intersectable=%s", 568 | Boolean.toString(canBeMerge), Boolean.toString(intersectable(sourceRange))); 569 | } 570 | 571 | if (canBeMerge) 572 | return mergeFromInternal(sourceRange); 573 | 574 | 575 | return false; 576 | } 577 | 578 | 579 | public boolean intersectable(CodeRange range) { 580 | if (this.rangeEnd < range.rangeStart) { 581 | return false; 582 | } 583 | 584 | if (this.rangeStart > range.rangeEnd) { 585 | return false; 586 | } 587 | 588 | return true; 589 | } 590 | 591 | public void unmapFromAddressMap() { 592 | 593 | for (InstructionInfo ii : this.densityList) { 594 | this.removeMapIfAvailable(ii); 595 | } 596 | } 597 | 598 | public boolean paintRange(Painter painter) { 599 | 600 | for (InstructionInfo inst : this.densityList) { 601 | 602 | if (!painter.paint(inst)) 603 | return false; 604 | } 605 | 606 | return true; 607 | } 608 | 609 | public void clearPaint() { 610 | for (InstructionInfo inst : this.densityList) { 611 | DragonHelper.clearInstructionBackgroundColor(inst.getAddr()); 612 | } 613 | } 614 | 615 | public void setName(String rangeName) { 616 | this.name = rangeName; 617 | } 618 | 619 | public final long getRangeStart() { 620 | return this.rangeStart; 621 | } 622 | 623 | public final int getRangeSize() { 624 | return (int)this.rangeSize; 625 | } 626 | 627 | public final String getName() { 628 | return this.name; 629 | } 630 | 631 | public CoverageData getContainerCoverage() { 632 | return this.container; 633 | } 634 | 635 | @Override 636 | public void close() throws Exception { 637 | this.densityList.clear(); 638 | } 639 | 640 | @Override 641 | public String toString() { 642 | return String.format("(Start: %x, End: %x, Size: %d)", this.rangeStart, this.rangeEnd, this.rangeSize); 643 | } 644 | 645 | } 646 | -------------------------------------------------------------------------------- /src/main/java/dragondance/eng/DragonHelper.java: -------------------------------------------------------------------------------- 1 | package dragondance.eng; 2 | 3 | import java.awt.Color; 4 | import java.awt.Component; 5 | import java.awt.EventQueue; 6 | import java.io.BufferedReader; 7 | import java.io.File; 8 | import java.io.IOException; 9 | import java.io.InputStreamReader; 10 | import java.io.PrintWriter; 11 | import java.io.StringWriter; 12 | import java.lang.reflect.InvocationTargetException; 13 | import java.net.URL; 14 | import java.nio.file.Path; 15 | import java.nio.file.Paths; 16 | import java.util.ArrayList; 17 | import java.util.List; 18 | import java.util.concurrent.atomic.AtomicBoolean; 19 | 20 | import javax.swing.SwingUtilities; 21 | 22 | import docking.widgets.OptionDialog; 23 | import docking.widgets.filechooser.GhidraFileChooser; 24 | import docking.widgets.filechooser.GhidraFileChooserMode; 25 | import dragondance.Globals; 26 | import dragondance.StringResources; 27 | import dragondance.exceptions.InvalidInstructionAddress; 28 | import dragondance.util.Util; 29 | import generic.concurrent.GThreadPool; 30 | import generic.jar.ResourceFile; 31 | import generic.json.JSONError; 32 | import generic.json.JSONParser; 33 | import generic.json.JSONToken; 34 | import ghidra.app.plugin.core.colorizer.ColorizingService; 35 | import ghidra.app.script.GhidraScript; 36 | import ghidra.app.script.GhidraScriptProvider; 37 | import ghidra.app.script.GhidraScriptUtil; 38 | import ghidra.app.script.GhidraState; 39 | import ghidra.app.services.ConsoleService; 40 | import ghidra.app.services.GoToService; 41 | import ghidra.framework.plugintool.PluginTool; 42 | import ghidra.program.flatapi.FlatProgramAPI; 43 | import ghidra.program.model.address.Address; 44 | import ghidra.program.model.address.AddressSet; 45 | import ghidra.program.model.listing.CodeUnit; 46 | import ghidra.program.model.listing.Instruction; 47 | import ghidra.program.model.mem.MemoryBlock; 48 | import ghidra.util.Msg; 49 | import ghidra.util.SystemUtilities; 50 | import ghidra.util.task.DummyCancellableTaskMonitor; 51 | import ghidra.util.task.TaskMonitor; 52 | 53 | 54 | public class DragonHelper { 55 | private static PluginTool tool = null; 56 | private static FlatProgramAPI fapi = null; 57 | private static GThreadPool tpool = null; 58 | 59 | 60 | public static void init(PluginTool pluginTool, FlatProgramAPI api) { 61 | DragonHelper.tool = pluginTool; 62 | DragonHelper.fapi = api; 63 | } 64 | 65 | public static int startTransaction(String name) { 66 | return fapi.getCurrentProgram().startTransaction(name); 67 | } 68 | 69 | public static void finishTransaction(int id, boolean commit) { 70 | fapi.getCurrentProgram().endTransaction(id, commit); 71 | } 72 | 73 | 74 | public static boolean runScript(String scriptName, String[] scriptArguments, GhidraState scriptState) 75 | throws Exception { 76 | 77 | //set dummy printwriter to satisfy ghidra scripting api 78 | 79 | StringWriter sw = new StringWriter(); 80 | PrintWriter writer = new PrintWriter(sw); 81 | 82 | TaskMonitor monitor = new DummyCancellableTaskMonitor(); 83 | 84 | Path p = Paths.get( 85 | System.getProperty("user.dir"), 86 | "Ghidra", 87 | "Extensions", 88 | "dragondance", 89 | "ghidra_scripts", 90 | scriptName 91 | ); 92 | 93 | 94 | //List dirs = GhidraScriptUtil.getScriptSourceDirectories(); 95 | 96 | ResourceFile scriptSource = new ResourceFile(p.toAbsolutePath().toString()); 97 | 98 | 99 | if (scriptSource.exists()) { 100 | GhidraScriptProvider gsp = GhidraScriptUtil.getProvider(scriptSource); 101 | 102 | if (gsp == null) { 103 | writer.close(); 104 | throw new IOException("Attempting to run subscript '" + scriptName + 105 | "': unable to run this script type."); 106 | } 107 | 108 | GhidraScript script = gsp.getScriptInstance(scriptSource, writer); 109 | script.setScriptArgs(scriptArguments); 110 | 111 | script.execute(scriptState, monitor, writer); 112 | 113 | } 114 | else 115 | { 116 | return false; 117 | } 118 | 119 | return true; 120 | } 121 | 122 | public static String getProgramName() { 123 | return fapi.getCurrentProgram().getDomainFile().getName(); 124 | } 125 | 126 | 127 | public static GThreadPool getTPool() { 128 | if (tpool == null) { 129 | tpool = GThreadPool.getPrivateThreadPool("DragonDance"); 130 | tpool.setMaxThreadCount(4); 131 | } 132 | 133 | return tpool; 134 | } 135 | 136 | public static void queuePoolWorkItem(Runnable r) { 137 | getTPool().submit(r); 138 | } 139 | 140 | public static boolean runOnSwingThread(Runnable r, boolean waitExecution) { 141 | if (waitExecution) { 142 | try { 143 | SwingUtilities.invokeAndWait(r); 144 | } catch (InvocationTargetException | InterruptedException e) { 145 | return false; 146 | } 147 | } 148 | else { 149 | SystemUtilities.runIfSwingOrPostSwingLater(r); 150 | } 151 | 152 | return true; 153 | } 154 | 155 | public static void showMessage(String message, Object...args) { 156 | if (isUiDispatchThread()) 157 | Msg.showInfo(DragonHelper.class, null, "Dragon Dance", String.format(message, args)); 158 | else 159 | showMessageOnSwingThread(message,args); 160 | } 161 | 162 | public static void showWarning(String message, Object...args) { 163 | if (isUiDispatchThread()) 164 | Msg.showWarn(DragonHelper.class, null, "Dragon Dance", String.format(message, args)); 165 | else 166 | showWarningOnSwingThread(message,args); 167 | } 168 | 169 | public static void showMessageOnSwingThread(String message, Object ...args) { 170 | 171 | try { 172 | SwingUtilities.invokeAndWait(() -> { 173 | showMessage(message, args); 174 | }); 175 | } catch (InvocationTargetException | InterruptedException e) { 176 | 177 | } 178 | } 179 | 180 | public static void showWarningOnSwingThread(String message, Object ...args) { 181 | 182 | try { 183 | SwingUtilities.invokeAndWait(() -> { 184 | showWarning(message, args); 185 | }); 186 | } catch (InvocationTargetException | InterruptedException e) { 187 | 188 | } 189 | } 190 | 191 | public static boolean showYesNoMessage(String title, String messageFormat, Object...args) { 192 | 193 | String message; 194 | 195 | message = String.format(messageFormat, args); 196 | 197 | AtomicBoolean yesno = new AtomicBoolean(); 198 | 199 | Runnable r = () -> { 200 | int choice = OptionDialog.showYesNoDialog(null, title, message); 201 | yesno.set(choice == OptionDialog.OPTION_ONE); 202 | }; 203 | 204 | SystemUtilities.runSwingNow(r); 205 | 206 | return yesno.get(); 207 | } 208 | 209 | public static void printConsole(String format, Object... args) { 210 | 211 | String message = String.format(format, args); 212 | 213 | if (tool == null) { 214 | return; 215 | } 216 | 217 | ConsoleService console = tool.getService(ConsoleService.class); 218 | 219 | if (console == null) { 220 | return; 221 | } 222 | 223 | console.print(message); 224 | 225 | } 226 | 227 | public static boolean isValidExecutableSectionAddress(long addr) { 228 | if (addr < getImageBase().getOffset()) 229 | return false; 230 | 231 | if (addr >= getImageEnd().getOffset()) 232 | return false; 233 | 234 | return isCodeSectionAddress(addr); 235 | } 236 | 237 | 238 | public static boolean goToAddress(long addr) { 239 | GoToService gotoService = tool.getService(GoToService.class); 240 | 241 | if (gotoService==null) 242 | return false; 243 | 244 | if (!isValidExecutableSectionAddress(addr)) { 245 | showWarning("%x is not valid offset.",addr); 246 | return false; 247 | } 248 | 249 | 250 | if (getInstructionNoThrow(getAddress(addr),true) == null) { 251 | return false; 252 | } 253 | 254 | return gotoService.goTo(getAddress(addr)); 255 | } 256 | 257 | public static Address getAddress(long addrValue) { 258 | return fapi.toAddr(addrValue); 259 | } 260 | 261 | public static String askFile(Component parent, String title, String okButtonText) { 262 | 263 | GhidraFileChooser gfc = new GhidraFileChooser(parent); 264 | 265 | if (!Globals.LastFileDialogPath.isEmpty()) { 266 | File def = new File(Globals.LastFileDialogPath); 267 | gfc.setSelectedFile(def); 268 | } 269 | 270 | gfc.setTitle(title); 271 | gfc.setApproveButtonText(okButtonText); 272 | gfc.setFileSelectionMode(GhidraFileChooserMode.FILES_ONLY); 273 | 274 | File file = gfc.getSelectedFile(); 275 | 276 | if (file == null) { 277 | return null; 278 | } 279 | 280 | if (!file.exists()) 281 | return null; 282 | 283 | Globals.LastFileDialogPath = Util.getDirectoryOfFile(file.getAbsolutePath()); 284 | 285 | if (Globals.LastFileDialogPath == null) 286 | Globals.LastFileDialogPath = System.getProperty("user.dir"); 287 | 288 | return file.getAbsolutePath(); 289 | } 290 | 291 | public static AddressSet makeAddressSet(long addr, int size) { 292 | Address bAddr, eAddr; 293 | 294 | bAddr = fapi.toAddr(addr); 295 | eAddr = bAddr.add(size); 296 | 297 | AddressSet addrSet = new AddressSet(); 298 | addrSet.add(bAddr, eAddr); 299 | 300 | return addrSet; 301 | } 302 | 303 | public static String getExecutableMD5Hash() { 304 | return fapi.getCurrentProgram().getExecutableMD5(); 305 | } 306 | 307 | public static Address getImageBase() { 308 | if (Globals.WithoutGhidra) 309 | return getAddress(0x10000000); 310 | 311 | return fapi.getCurrentProgram().getImageBase(); 312 | } 313 | 314 | public static Address getImageEnd() { 315 | return fapi.getCurrentProgram().getMaxAddress(); 316 | } 317 | 318 | public static InstructionContext getInstruction(long addr, boolean throwEx) throws InvalidInstructionAddress { 319 | return getInstruction(fapi.toAddr(addr),throwEx); 320 | } 321 | 322 | public static InstructionContext getInstruction(Address addr, boolean throwEx) throws InvalidInstructionAddress { 323 | return getInstruction(addr,throwEx,false); 324 | } 325 | 326 | private static InstructionContext getInstructionNoThrow(Address addr, boolean icall) { 327 | InstructionContext inst = null; 328 | 329 | try { 330 | inst = getInstruction(addr,false,icall); 331 | } 332 | catch (Exception e) {} 333 | 334 | return inst; 335 | } 336 | 337 | private static InstructionContext getInstruction(Address addr, boolean throwEx, boolean icall) throws InvalidInstructionAddress { 338 | InstructionContext ictx; 339 | 340 | if (addr == null) 341 | return null; 342 | 343 | long naddr=addr.getOffset(); 344 | 345 | Instruction inst = fapi.getInstructionAt(addr); 346 | CodeUnit cu; 347 | 348 | if (inst == null) { 349 | 350 | if (icall) { 351 | 352 | if (throwEx) 353 | throw new InvalidInstructionAddress(naddr); 354 | 355 | return null; 356 | } 357 | 358 | if (!isCodeSectionAddress(naddr)) { 359 | 360 | if (throwEx) 361 | throw new InvalidInstructionAddress(naddr); 362 | 363 | return null; 364 | } 365 | else if (isInDisassembledRange(naddr)) { 366 | //invalid instruction 367 | if (throwEx) 368 | throw new InvalidInstructionAddress(naddr); 369 | 370 | return null; 371 | } 372 | 373 | DragonHelper.goToAddress(naddr); 374 | 375 | boolean choice = DragonHelper.showYesNoMessage("Warning", 376 | StringResources.INVALID_CODE_ADDRESS_FIX_MESSAGE, 377 | naddr); 378 | 379 | if (choice) { 380 | 381 | if (!disassemble(naddr)) { 382 | 383 | if (throwEx) 384 | throw new InvalidInstructionAddress(naddr); 385 | 386 | return null; 387 | } 388 | 389 | 390 | return getInstruction(addr,throwEx,true); 391 | } 392 | 393 | 394 | return null; 395 | } 396 | 397 | cu = fapi.getCurrentProgram().getListing().getCodeUnitAt(addr); 398 | 399 | ictx = new InstructionContext(inst,cu); 400 | 401 | return ictx; 402 | } 403 | 404 | public static boolean isUiDispatchThread() { 405 | return EventQueue.isDispatchThread(); 406 | } 407 | 408 | public static boolean disassemble(long addr) { 409 | 410 | boolean result; 411 | AtomicBoolean ret = new AtomicBoolean(); 412 | 413 | int transId = startTransaction("DgDisasm"); 414 | 415 | if (EventQueue.isDispatchThread()) { 416 | result = fapi.disassemble(getAddress(addr)); 417 | } 418 | else { 419 | 420 | Runnable r = () -> { 421 | 422 | ret.set(fapi.disassemble(getAddress(addr))); 423 | 424 | }; 425 | 426 | runOnSwingThread(r, true); 427 | 428 | result = ret.get(); 429 | } 430 | 431 | 432 | finishTransaction(transId,result); 433 | 434 | return result; 435 | } 436 | 437 | public static List getExecutableMemoryBlocks() { 438 | MemoryBlock[] blocks = fapi.getCurrentProgram().getMemory().getBlocks(); 439 | List memList = new ArrayList(); 440 | 441 | for (MemoryBlock block : blocks) { 442 | if (block.isExecute()) { 443 | memList.add(block); 444 | } 445 | } 446 | 447 | return memList; 448 | } 449 | 450 | public static boolean isCodeSectionAddress(long addr) { 451 | //.text, .init .fini __text 452 | boolean status=false; 453 | 454 | List execBlocks = getExecutableMemoryBlocks(); 455 | 456 | for (MemoryBlock mb : execBlocks) { 457 | if (addr >= mb.getStart().getOffset() && addr < mb.getEnd().getOffset()) { 458 | status=true; 459 | break; 460 | } 461 | } 462 | 463 | execBlocks.clear(); 464 | 465 | return status; 466 | } 467 | 468 | public static boolean isInDisassembledRange(long addr) { 469 | int iMax=15; 470 | 471 | Address gaddr; 472 | InstructionContext inst; 473 | 474 | gaddr = getAddress(addr); 475 | 476 | inst = getInstructionNoThrow(gaddr,true); 477 | 478 | if (inst != null) 479 | return true; 480 | 481 | while (iMax-- > 0) { 482 | gaddr = gaddr.subtract(1); 483 | inst = getInstructionNoThrow(gaddr,true); 484 | 485 | if (inst != null) 486 | break; 487 | } 488 | 489 | if (inst == null) 490 | return false; 491 | 492 | gaddr = gaddr.add(inst.getSize()); 493 | 494 | inst = getInstructionNoThrow(gaddr,true); 495 | 496 | if (inst == null) 497 | return false; 498 | 499 | if (addr <= inst.getAddress()) { 500 | return true; 501 | } 502 | 503 | iMax = 15; 504 | 505 | while (iMax-- > 0) { 506 | gaddr = gaddr.add(inst.getSize()); 507 | 508 | inst = getInstructionNoThrow(gaddr,true); 509 | 510 | if (inst != null) { 511 | if (inst.getAddress() <= addr) { 512 | return true; 513 | } 514 | } 515 | else 516 | break; 517 | } 518 | 519 | 520 | return false; 521 | } 522 | 523 | public static boolean setInstructionBackgroundColor(long addr, Color color) { 524 | 525 | Address ba; 526 | 527 | ColorizingService colorService = tool.getService(ColorizingService.class); 528 | 529 | if (colorService == null) { 530 | return false; 531 | } 532 | 533 | ba = getAddress(addr); 534 | 535 | colorService.setBackgroundColor(ba, ba, color); 536 | 537 | return true; 538 | } 539 | 540 | public static boolean clearInstructionBackgroundColor(long addr) { 541 | 542 | Address ba; 543 | 544 | ColorizingService colorService = tool.getService(ColorizingService.class); 545 | 546 | if (colorService == null) { 547 | return false; 548 | } 549 | 550 | ba = getAddress(addr); 551 | 552 | colorService.clearBackgroundColor(ba, ba); 553 | 554 | return true; 555 | } 556 | 557 | public static String getStringFromURL(String url) { 558 | try { 559 | URL u = new URL(url); 560 | 561 | StringBuilder sb = new StringBuilder(); 562 | BufferedReader reader = new BufferedReader(new InputStreamReader(u.openStream())); 563 | 564 | String nextLine = ""; 565 | 566 | while ((nextLine = reader.readLine()) != null) { 567 | sb.append(nextLine + "\n"); 568 | } 569 | 570 | return sb.toString(); 571 | 572 | } catch (IOException e) { 573 | 574 | } 575 | 576 | return null; 577 | } 578 | 579 | public static Object parseJson(String jsonData) { 580 | char[] cbuf = new char[jsonData.length()]; 581 | Object obj = null; 582 | JSONParser parser = new JSONParser(); 583 | List tokens = new ArrayList(); 584 | jsonData.getChars(0, jsonData.length(), cbuf, 0); 585 | 586 | if (parser.parse(cbuf, tokens) != JSONError.JSMN_SUCCESS) { 587 | return null; 588 | } 589 | 590 | try { 591 | obj = parser.convert(cbuf, tokens); 592 | } 593 | catch (Exception ex) { 594 | tokens.clear(); 595 | return null; 596 | } 597 | 598 | tokens.clear(); 599 | return obj; 600 | } 601 | } 602 | -------------------------------------------------------------------------------- /src/main/java/dragondance/eng/InstructionContext.java: -------------------------------------------------------------------------------- 1 | package dragondance.eng; 2 | 3 | import ghidra.program.model.listing.CodeUnit; 4 | import ghidra.program.model.listing.Instruction; 5 | 6 | public class InstructionContext { 7 | @SuppressWarnings("unused") 8 | private Instruction instruction; 9 | private CodeUnit codeUnit; 10 | private InstructionInfo info; 11 | 12 | public InstructionContext(Instruction inst, CodeUnit cu, InstructionInfo info) { 13 | this.instruction = inst; 14 | this.codeUnit = cu; 15 | 16 | if (info == null) { 17 | info = new InstructionInfo(null,inst.getAddress().getOffset(),cu.getLength(),0); 18 | } 19 | 20 | this.info = info; 21 | } 22 | 23 | public InstructionContext(Instruction inst, CodeUnit cu) { 24 | this(inst,cu,null); 25 | } 26 | 27 | public final int getSize() { 28 | return this.codeUnit.getLength(); 29 | } 30 | 31 | public final long getAddress() { 32 | return this.info.getAddr(); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/main/java/dragondance/eng/InstructionInfo.java: -------------------------------------------------------------------------------- 1 | package dragondance.eng; 2 | 3 | public class InstructionInfo { 4 | 5 | private long addr; 6 | private int size; 7 | private int density; 8 | private CodeRange container; 9 | 10 | public static InstructionInfo cloneFor(InstructionInfo inst, CodeRange containerRange) { 11 | InstructionInfo cloneInst = new InstructionInfo(containerRange,inst.addr,inst.size, inst.density); 12 | return cloneInst; 13 | } 14 | 15 | public InstructionInfo(CodeRange containerRange, long a, int s, int d) { 16 | this.container = containerRange; 17 | this.addr=a; 18 | this.size=s; 19 | this.setDensity(d); 20 | } 21 | 22 | public long getAddr() { 23 | return this.addr; 24 | } 25 | 26 | public int getSize() { 27 | return this.size; 28 | } 29 | 30 | public int getDensity() { 31 | return this.density; 32 | } 33 | 34 | private void onDensityChanged() { 35 | 36 | if (this.container != null) 37 | this.container.getContainerCoverage().setMaxDensity(this.density); 38 | 39 | } 40 | 41 | public void setDensity(int value) { 42 | this.density=value; 43 | onDensityChanged(); 44 | } 45 | 46 | public void incrementDensityBy(int amount) { 47 | this.density += amount; 48 | onDensityChanged(); 49 | } 50 | 51 | public void incrementDensity() { 52 | this.density++; 53 | onDensityChanged(); 54 | } 55 | 56 | public final boolean hasOwnerRange() { 57 | return this.container != null; 58 | } 59 | 60 | public CodeRange getOwnerCodeRange() { 61 | return this.container; 62 | } 63 | 64 | @Override 65 | public String toString() { 66 | return String.format("(%x, %d [%x])",this.addr, this.size,this.addr + this.size); 67 | } 68 | } -------------------------------------------------------------------------------- /src/main/java/dragondance/eng/Painter.java: -------------------------------------------------------------------------------- 1 | package dragondance.eng; 2 | 3 | import java.awt.Color; 4 | import java.io.FileNotFoundException; 5 | 6 | import dragondance.Globals; 7 | import dragondance.eng.session.SessionManager; 8 | 9 | public class Painter { 10 | 11 | private static final float BRIGHTNESS = 1.0f; 12 | private static final float SATURATION = 0.2f; 13 | 14 | public static final int CP_USE_MAX_DENSITY = 0; 15 | public static final int CP_USE_THRESHOLD_VALUE = 1; 16 | 17 | public static final int PAINT_MODE_DEFAULT=0; 18 | public static final int PAINT_MODE_INTERSECTION=1; 19 | public static final int PAINT_MODE_MAX=PAINT_MODE_INTERSECTION; 20 | 21 | 22 | private int colorPolicy; 23 | private boolean testSampleGen=false; 24 | private int testMaxDensity=0; 25 | private int mode=PAINT_MODE_DEFAULT; 26 | 27 | public Painter() { 28 | this(false); 29 | } 30 | 31 | public Painter(boolean testSampleMode) { 32 | this.testSampleGen=testSampleMode; 33 | this.colorPolicy = CP_USE_THRESHOLD_VALUE; 34 | } 35 | 36 | //Hsb to Rgb conversion algorithm 37 | //#https://en.wikipedia.org/wiki/HSL_and_HSV#From_HSV 38 | private Color hsbToRgb(float hue, float sat, float brig) { 39 | float c = sat * brig; 40 | float m = brig-c; 41 | 42 | float r,g,b; 43 | 44 | hue /= 60.0; 45 | 46 | int i = ((Double)Math.floor(hue)).intValue(); 47 | 48 | float x = c * (1-Math.abs(hue % 2 - 1)); 49 | 50 | switch (i) { 51 | case 0: 52 | r=c; 53 | g=x; 54 | b=0; 55 | break; 56 | case 1: 57 | r = x; 58 | g = c; 59 | b = 0; 60 | break; 61 | case 2: 62 | r = 0; 63 | g = c; 64 | b = x; 65 | break; 66 | case 3: 67 | r = 0; 68 | g = x; 69 | b = c; 70 | break; 71 | case 4: 72 | r = x; 73 | g = 0; 74 | b = c; 75 | break; 76 | default: 77 | r = c; 78 | g = 0; 79 | b = x; 80 | break; 81 | } 82 | 83 | r = (r + m) * 255.0f; 84 | g = (g + m) * 255.0f; 85 | b = (b + m) * 255.0f; 86 | 87 | return new Color((int)r,(int)g,(int)b); 88 | } 89 | 90 | private int getMaxDensity() { 91 | if (this.testSampleGen) 92 | return this.testMaxDensity; 93 | 94 | return SessionManager. 95 | getActiveSession(). 96 | getActiveCoverage().getMaxDensity(); 97 | } 98 | 99 | private Color getHeatColorThreshold(int density) { 100 | int maxDensity; 101 | float heatHue,factor; 102 | 103 | maxDensity = getMaxDensity(); 104 | 105 | factor = (Globals.MAX_HUE / maxDensity) / 2; 106 | 107 | if (factor < 1.0f) 108 | factor = 1.0f; 109 | 110 | heatHue = Globals.MIN_HUE + (density * factor); 111 | 112 | if (heatHue > Globals.MAX_HUE) 113 | heatHue = Globals.MAX_HUE; 114 | 115 | return hsbToRgb(heatHue,SATURATION,BRIGHTNESS); 116 | } 117 | 118 | private Color getHeatColorMaxDensity(int density) { 119 | float heatHue, dp,hp,hdiff; 120 | 121 | hdiff = Globals.MAX_HUE - Globals.MIN_HUE; 122 | 123 | dp = (density / (float)getMaxDensity()) * 100.0f; 124 | hp = (dp * hdiff) / 100.0f; 125 | heatHue = Globals.MIN_HUE + hp; 126 | 127 | return hsbToRgb(heatHue,SATURATION,BRIGHTNESS); 128 | } 129 | 130 | private Color getHeatColor(int density) { 131 | 132 | switch (this.colorPolicy) { 133 | case Painter.CP_USE_MAX_DENSITY: 134 | return getHeatColorMaxDensity(density); 135 | case Painter.CP_USE_THRESHOLD_VALUE: 136 | return getHeatColorThreshold(density); 137 | } 138 | 139 | return Color.WHITE; 140 | } 141 | 142 | public boolean paint(InstructionInfo inst) { 143 | Color color; 144 | 145 | if (this.mode == PAINT_MODE_DEFAULT) { 146 | color = getHeatColor(inst.getDensity()); 147 | } 148 | else { 149 | color = hsbToRgb(360.0f,0.72f,0.60f); 150 | } 151 | 152 | return DragonHelper.setInstructionBackgroundColor(inst.getAddr(), color); 153 | } 154 | 155 | public int setMode(int newMode) { 156 | 157 | int oldMode = this.mode; 158 | 159 | if (newMode < 0 || newMode > PAINT_MODE_MAX) 160 | return -1; 161 | 162 | this.mode = newMode; 163 | return oldMode; 164 | } 165 | 166 | //Dont use this method. 167 | public void generateTestSample(int maxDensity, String file) { 168 | this.testMaxDensity=maxDensity; 169 | 170 | java.io.File ff = new java.io.File(file); 171 | java.io.FileOutputStream fos = null; 172 | 173 | try { 174 | fos = new java.io.FileOutputStream(ff,true); 175 | } catch (FileNotFoundException e) { 176 | // TODO Auto-generated catch block 177 | e.printStackTrace(); 178 | } 179 | 180 | java.io.PrintWriter pw = new java.io.PrintWriter(fos); 181 | 182 | for (int i=1;i"); 187 | pw.write(String.valueOf(i)); 188 | pw.write("


"); 189 | 190 | } 191 | 192 | pw.close(); 193 | 194 | } 195 | } 196 | -------------------------------------------------------------------------------- /src/main/java/dragondance/eng/session/Session.java: -------------------------------------------------------------------------------- 1 | package dragondance.eng.session; 2 | 3 | import java.io.FileNotFoundException; 4 | import java.lang.reflect.InvocationTargetException; 5 | import java.util.ArrayList; 6 | import java.util.List; 7 | 8 | import dragondance.Log; 9 | import dragondance.datasource.CoverageData; 10 | import dragondance.datasource.CoverageDataSource; 11 | import dragondance.datasource.DynamorioDataSource; 12 | import dragondance.datasource.PintoolDataSource; 13 | import dragondance.eng.Painter; 14 | 15 | 16 | public class Session { 17 | 18 | private String sessionName; 19 | private String imageName; 20 | private int coverageIndex=1; 21 | 22 | private List coverageSources; 23 | 24 | private CoverageData activeCoverage=null; 25 | 26 | private Painter painter=null; 27 | 28 | public static Session createNew(String name, String imageName) { 29 | Session sess = new Session(); 30 | 31 | sess.imageName = imageName; 32 | sess.sessionName = name; 33 | sess.coverageSources = new ArrayList(); 34 | 35 | SessionManager.registerSession(sess); 36 | 37 | return sess; 38 | } 39 | 40 | private Class getDatasourceAdapter(int type) { 41 | switch (type) 42 | { 43 | case CoverageDataSource.SOURCE_TYPE_DYNA: 44 | return DynamorioDataSource.class; 45 | case CoverageDataSource.SOURCE_TYPE_PINTOOL: 46 | return PintoolDataSource.class; 47 | } 48 | 49 | return null; 50 | } 51 | 52 | public static String getCoverageTypeString(int typeValue) { 53 | switch (typeValue) { 54 | case CoverageDataSource.SOURCE_TYPE_DYNA: 55 | return "Dynamorio"; 56 | case CoverageDataSource.SOURCE_TYPE_PINTOOL: 57 | return "Pintool"; 58 | } 59 | 60 | return "Unknown"; 61 | } 62 | 63 | public static Session open(String sessionDatabase) throws Exception { 64 | throw new Exception("Session.open not implemented yet."); 65 | } 66 | 67 | 68 | public CoverageData addCoverageData(String fileName) throws FileNotFoundException { 69 | int sourceType = CoverageDataSource.detectCoverageDataFileType(fileName); 70 | 71 | if (sourceType == -1) 72 | return null; 73 | 74 | Class clazz = getDatasourceAdapter(sourceType); 75 | CoverageDataSource dataSource; 76 | CoverageData coverage; 77 | 78 | if (clazz == null) 79 | return null; 80 | 81 | 82 | try { 83 | dataSource = (CoverageDataSource)clazz.getDeclaredConstructor(String.class, String.class).newInstance(fileName, this.imageName); 84 | } catch (InstantiationException | IllegalAccessException | IllegalArgumentException | InvocationTargetException 85 | | NoSuchMethodException | SecurityException e) { 86 | 87 | Log.println(e.getMessage()); 88 | return null; 89 | } 90 | 91 | dataSource.setId(coverageIndex++); 92 | 93 | coverage = new CoverageData(dataSource); 94 | 95 | this.coverageSources.add(coverage); 96 | 97 | if (this.activeCoverage == null) { 98 | this.activeCoverage = coverage; 99 | } 100 | 101 | return coverage; 102 | } 103 | 104 | public CoverageData getCoverage(int id) { 105 | CoverageData covData; 106 | 107 | for (int i=0;i -1) 179 | this.painter.setMode(oldMode); 180 | } 181 | 182 | return true; 183 | } 184 | 185 | public boolean isActiveCoverage(CoverageData coverage) { 186 | return this.activeCoverage == coverage; 187 | } 188 | 189 | public void setPainter(Painter painter) { 190 | this.painter = painter; 191 | } 192 | 193 | public String getName() { 194 | return this.sessionName; 195 | } 196 | 197 | public CoverageData tryGetPreviouslyLoadedCoverage(String fileName) { 198 | for (CoverageData cov : this.coverageSources) { 199 | if (!cov.isLogicalCoverageData() && cov.getSourceFilePath().equals(fileName)) 200 | return cov; 201 | } 202 | 203 | return null; 204 | } 205 | 206 | public CoverageData getCoverageByName(String name) { 207 | for (CoverageData cov : this.coverageSources) { 208 | if (cov.getName().equals(name)) { 209 | return cov; 210 | } 211 | } 212 | 213 | return null; 214 | } 215 | 216 | public void close() throws Exception { 217 | 218 | SessionManager.deregisterSession(this); 219 | 220 | for (CoverageData cd : this.coverageSources) { 221 | cd.close(); 222 | } 223 | 224 | this.coverageSources.clear(); 225 | } 226 | } 227 | -------------------------------------------------------------------------------- /src/main/java/dragondance/eng/session/SessionManager.java: -------------------------------------------------------------------------------- 1 | package dragondance.eng.session; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | 6 | public class SessionManager { 7 | private static List sessions; 8 | private static Session activeSession; 9 | 10 | static { 11 | SessionManager.sessions = new ArrayList(); 12 | } 13 | 14 | public static void registerSession(Session session) { 15 | sessions.add(session); 16 | 17 | if (sessions.size()==1) { 18 | activeSession=session; 19 | } 20 | } 21 | 22 | public static void deregisterSession(Session session) { 23 | 24 | if (session == null) 25 | return; 26 | 27 | sessions.remove(session); 28 | } 29 | 30 | public static Session getActiveSession() { 31 | return activeSession; 32 | } 33 | 34 | public static boolean switchSession(String name) { 35 | return false; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/main/java/dragondance/exceptions/DragonDanceScriptRuntimeException.java: -------------------------------------------------------------------------------- 1 | package dragondance.exceptions; 2 | 3 | public class DragonDanceScriptRuntimeException extends RuntimeException { 4 | public DragonDanceScriptRuntimeException(String message) { 5 | super("Dragon Dance script runtime error: " + message); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /src/main/java/dragondance/exceptions/InvalidInstructionAddress.java: -------------------------------------------------------------------------------- 1 | package dragondance.exceptions; 2 | 3 | public class InvalidInstructionAddress extends Exception { 4 | 5 | public InvalidInstructionAddress(long addr) { 6 | super(String.format("Invalid instruction address (%x)",addr)); 7 | } 8 | 9 | } 10 | -------------------------------------------------------------------------------- /src/main/java/dragondance/exceptions/OperationAbortedException.java: -------------------------------------------------------------------------------- 1 | package dragondance.exceptions; 2 | 3 | public class OperationAbortedException extends Exception { 4 | 5 | public OperationAbortedException() { 6 | this("Operation aborted"); 7 | } 8 | 9 | public OperationAbortedException(String message) { 10 | super(message); 11 | } 12 | 13 | 14 | } 15 | -------------------------------------------------------------------------------- /src/main/java/dragondance/exceptions/ScriptParserException.java: -------------------------------------------------------------------------------- 1 | package dragondance.exceptions; 2 | 3 | public class ScriptParserException extends Exception { 4 | 5 | public ScriptParserException(String msg, Object ...args) { 6 | super(String.format(msg, args)); 7 | } 8 | 9 | 10 | } 11 | -------------------------------------------------------------------------------- /src/main/java/dragondance/scripting/DragonDanceScriptParser.java: -------------------------------------------------------------------------------- 1 | package dragondance.scripting; 2 | 3 | import java.util.LinkedList; 4 | import java.util.List; 5 | import java.util.ListIterator; 6 | import java.util.Stack; 7 | 8 | import dragondance.Log; 9 | import dragondance.components.GuiAffectedOpInterface; 10 | import dragondance.exceptions.ScriptParserException; 11 | import dragondance.scripting.functions.BuiltinArg; 12 | import dragondance.scripting.functions.BuiltinFunctionBase; 13 | 14 | class DDSToken { 15 | public final static int TOK_IDENTIFIER=0; 16 | public final static int TOK_BUILTIN=1; 17 | public final static int TOK_ASSIGN=2; 18 | public final static int TOK_OPEN_PARAN=3; 19 | public final static int TOK_CLOSE_PARAN=4; 20 | public final static int TOK_COMMA=5; 21 | public final static int TOK_STRING=6; 22 | public final static int TOK_INTEGER=7; 23 | 24 | public final static String[] TOK_STRINGS = { 25 | "Identifier (argument)", 26 | "Builtin", 27 | "=", 28 | "(", 29 | ")", 30 | ",", 31 | "String", 32 | "Integer" 33 | }; 34 | 35 | private String tok; 36 | private int type; 37 | private int line,pos; 38 | 39 | public DDSToken(String tok, int tokType, int line, int pos) { 40 | this.tok = tok; 41 | this.type = tokType; 42 | this.line = line; 43 | this.pos = pos; 44 | } 45 | 46 | public final int getType() { 47 | return this.type; 48 | } 49 | 50 | public final String getValue() { 51 | return this.tok; 52 | } 53 | 54 | public final long getAsNumber() { 55 | try { 56 | return Long.decode(this.tok).longValue(); 57 | } 58 | catch (Exception e) { 59 | Log.println(e.getMessage()); 60 | return 0; 61 | } 62 | } 63 | 64 | private ScriptParserException buildException(String format, Object ...args) { 65 | String msg = String.format(format, args); 66 | 67 | msg += "\n\n" + String.format("line: %d, position: %d",this.line,this.pos); 68 | 69 | return new ScriptParserException(msg); 70 | } 71 | 72 | public ScriptParserException illegalIdentifier() { 73 | return buildException(this.getValue() + " is an illegal identifier. it may contains invalid char or begins with digit"); 74 | } 75 | 76 | public ScriptParserException unexpectedToken(String expected) { 77 | return buildException("\"%s\": expected but \"%s\" came.",expected,this.getValue()); 78 | } 79 | 80 | public ScriptParserException expected(String expect) { 81 | return buildException("\"%s\" expected.",expect); 82 | } 83 | 84 | 85 | public ScriptParserException alreadyDeclared(String var) { 86 | return buildException("\"%s\" already declared"); 87 | } 88 | 89 | public ScriptParserException missingArgument() { 90 | return buildException("builtin functions must have at least 2 arguments"); 91 | } 92 | 93 | public ScriptParserException variableNotDeclared() { 94 | return buildException("\"%s\" is not declared",this.getValue()); 95 | } 96 | 97 | public ScriptParserException suspicious(String msg) { 98 | return buildException("Suspicious behaviour: %s",msg); 99 | } 100 | 101 | @Override 102 | public String toString() { 103 | return String.format("tok: %s, type: %d", tok,type); 104 | } 105 | 106 | 107 | } 108 | 109 | public class DragonDanceScriptParser { 110 | 111 | private static GuiAffectedOpInterface guisvc; 112 | 113 | private List tokens; 114 | private ListIterator node = null; 115 | private ScriptExecutionUnit execUnit = null; 116 | private Stack callingStack=null; 117 | private GuiAffectedOpInterface guiSvc=null; 118 | private int currLine=0,currPos=0; 119 | private DDSToken dummy = new DDSToken("",0,0,0); 120 | 121 | public static void setGuiSvc(GuiAffectedOpInterface gai) { 122 | guisvc = gai; 123 | } 124 | 125 | public static GuiAffectedOpInterface getGAI() { 126 | return guisvc; 127 | } 128 | 129 | public DragonDanceScriptParser() { 130 | this.tokens = new LinkedList(); 131 | this.callingStack = new Stack(); 132 | this.guiSvc = guisvc; 133 | 134 | } 135 | 136 | private void newExecUnit() { 137 | 138 | if (this.execUnit != null) 139 | DragonDanceScripting.addExecutionUnit(this.execUnit); 140 | 141 | this.execUnit = new ScriptExecutionUnit(); 142 | } 143 | 144 | private ScriptExecutionUnit getExecUnit() { 145 | if (this.execUnit == null) 146 | this.execUnit = new ScriptExecutionUnit(); 147 | 148 | return this.execUnit; 149 | } 150 | 151 | private void pushCallingStack(BuiltinFunctionBase func) { 152 | this.callingStack.push(func); 153 | } 154 | 155 | private BuiltinFunctionBase popCallingStack() { 156 | return this.callingStack.pop(); 157 | } 158 | 159 | private void pushToken(String s, int type) { 160 | int pos = this.currPos; 161 | 162 | if (s.length()>1) { 163 | pos -= s.length(); 164 | } 165 | 166 | tokens.add(new DDSToken(s,type,this.currLine,pos)); 167 | 168 | } 169 | 170 | private void pushToken(char c, int type) { 171 | pushToken(Character.toString(c),type); 172 | } 173 | 174 | 175 | private boolean isBufferedChar(char c) { 176 | if (Character.isLetterOrDigit(c)) 177 | return true; 178 | 179 | if (c == '-' || c == '+') 180 | return true; 181 | 182 | return false; 183 | } 184 | 185 | private void processStringBufferAsToken(StringBuffer sb, boolean quoteHint) { 186 | String sval = sb.toString(); 187 | sb.setLength(0); 188 | 189 | 190 | if (DragonDanceScripting.isBuiltin(sval)) { 191 | pushToken(sval,DDSToken.TOK_BUILTIN); 192 | } 193 | else if (quoteHint) { 194 | pushToken(sval,DDSToken.TOK_STRING); 195 | } 196 | else { 197 | 198 | try { 199 | Long.decode(sval); 200 | pushToken(sval,DDSToken.TOK_INTEGER); 201 | } 202 | catch (Exception e) { 203 | pushToken(sval,DDSToken.TOK_IDENTIFIER); 204 | } 205 | 206 | 207 | } 208 | 209 | } 210 | 211 | private boolean tokenize(String script) { 212 | StringBuffer sbuf = new StringBuffer(); 213 | char c; 214 | boolean quoteOn=false; 215 | 216 | for (int i=0;i0) { 236 | processStringBufferAsToken(sbuf,false); 237 | } 238 | 239 | if (c == '\"') 240 | quoteOn=true; 241 | else if (c == '(') 242 | pushToken(c, DDSToken.TOK_OPEN_PARAN); 243 | else if (c == ')') 244 | pushToken(c, DDSToken.TOK_CLOSE_PARAN); 245 | else if (c == '=') 246 | pushToken(c, DDSToken.TOK_ASSIGN); 247 | else if (c == ',') 248 | pushToken(c, DDSToken.TOK_COMMA); 249 | } 250 | 251 | this.currPos++; 252 | } 253 | 254 | if (sbuf.length() > 0) { 255 | processStringBufferAsToken(sbuf,quoteOn); 256 | } 257 | 258 | return this.tokens.size() > 0; 259 | } 260 | 261 | 262 | private String getExpectedTokenStrings(int [] types) { 263 | String s = ""; 264 | 265 | if (types.length < 1) 266 | return "token"; 267 | 268 | if (types.length == 1) 269 | return DDSToken.TOK_STRINGS[types[0]]; 270 | 271 | for (int i=0;i 0 && !justHint) { 303 | for (int i=0;i= builtin.requiredArgCount(true); 324 | } 325 | 326 | private void handleBuiltinArguments(BuiltinFunctionBase ownerFunc) throws ScriptParserException { 327 | DDSToken tok; 328 | boolean done=false; 329 | 330 | 331 | while (!done) { 332 | 333 | tok = nextToken(true, 334 | DDSToken.TOK_IDENTIFIER, 335 | DDSToken.TOK_STRING, 336 | DDSToken.TOK_INTEGER, 337 | DDSToken.TOK_CLOSE_PARAN 338 | ); 339 | 340 | if (tok.getType() == DDSToken.TOK_CLOSE_PARAN) { 341 | if (!isBuiltinSatisfiedByArglist(ownerFunc)) 342 | throw tok.missingArgument(); 343 | } 344 | else if (tok.getType() != DDSToken.TOK_IDENTIFIER && 345 | tok.getType() != DDSToken.TOK_STRING && 346 | tok.getType() != DDSToken.TOK_BUILTIN && 347 | tok.getType() != DDSToken.TOK_INTEGER) { 348 | 349 | throw tok.unexpectedToken("string, integer, coverage variable or builtin call as an argument"); 350 | } 351 | 352 | if (tok.getType() == DDSToken.TOK_IDENTIFIER) { 353 | if (!DragonDanceScripting.isVariableDeclared(tok.getValue())) 354 | throw tok.variableNotDeclared(); 355 | 356 | ScriptVariable sv = DragonDanceScripting.getVariable(tok.getValue()); 357 | BuiltinArg arg = new BuiltinArg(sv); 358 | ownerFunc.putArg(arg); 359 | } 360 | else if (tok.getType() == DDSToken.TOK_STRING) { 361 | BuiltinArg arg = new BuiltinArg(tok.getValue()); 362 | ownerFunc.putArg(arg); 363 | } 364 | else if (tok.getType() == DDSToken.TOK_INTEGER) { 365 | BuiltinArg arg = new BuiltinArg(tok.getAsNumber()); 366 | ownerFunc.putArg(arg); 367 | } 368 | else if (tok.getType() == DDSToken.TOK_BUILTIN) { //TOK_BUILTIN 369 | 370 | nextToken(false,DDSToken.TOK_OPEN_PARAN); 371 | 372 | BuiltinFunctionBase builtin = DragonDanceScripting.newInstance(tok.getValue(),this.guiSvc); 373 | BuiltinArg arg = new BuiltinArg(builtin); 374 | ownerFunc.putArg(arg); 375 | 376 | //handleBuiltinArgs 377 | 378 | if (this.callingStack.size() > 8) { 379 | throw tok.suspicious( 380 | String.format("is it really needed nested call depth? (%d)",this.callingStack.size())); 381 | } 382 | 383 | pushCallingStack(builtin); 384 | 385 | handleBuiltinArguments(builtin); 386 | 387 | nextToken(false,DDSToken.TOK_CLOSE_PARAN); 388 | 389 | popCallingStack(); 390 | } 391 | else if (tok.getType() == DDSToken.TOK_CLOSE_PARAN) { 392 | node.previous(); 393 | done=true; 394 | continue; 395 | } 396 | 397 | if (!node.hasNext()) { 398 | if (!isBuiltinSatisfiedByArglist(ownerFunc)) 399 | throw tok.expected(","); 400 | 401 | throw tok.expected(")"); 402 | } 403 | 404 | tok = node.next(); 405 | 406 | if (!isBuiltinSatisfiedByArglist(ownerFunc)) { 407 | if (tok.getType() != DDSToken.TOK_COMMA) { 408 | if (tok.getType() == DDSToken.TOK_CLOSE_PARAN) 409 | throw tok.missingArgument(); 410 | 411 | throw tok.unexpectedToken(","); 412 | } 413 | } 414 | else if (tok.getType() == DDSToken.TOK_CLOSE_PARAN) { 415 | done=true; 416 | node.previous(); 417 | } 418 | 419 | } 420 | } 421 | 422 | private void handleBuiltinCall(DDSToken tok) throws ScriptParserException { 423 | 424 | nextToken(false,DDSToken.TOK_OPEN_PARAN); 425 | 426 | //push paran stack 427 | 428 | //init execution unit only top builtin function 429 | if (this.callingStack.size() == 0) 430 | getExecUnit().initFunction(tok.getValue(), this.guiSvc); 431 | 432 | pushCallingStack(getExecUnit().getFunction()); 433 | 434 | BuiltinFunctionBase ownerFunc; 435 | 436 | ownerFunc = this.callingStack.peek(); 437 | 438 | handleBuiltinArguments(ownerFunc); 439 | 440 | nextToken(false,DDSToken.TOK_CLOSE_PARAN); 441 | 442 | newExecUnit(); 443 | 444 | popCallingStack(); 445 | } 446 | 447 | private void handleAssignee(DDSToken tok) throws ScriptParserException { 448 | DDSToken ntok; 449 | 450 | if (Character.isDigit(tok.getValue().charAt(0))) 451 | throw tok.illegalIdentifier(); 452 | 453 | if (!node.hasNext()) 454 | throw tok.expected("="); 455 | 456 | ntok = node.next(); 457 | 458 | if (ntok.getType() != DDSToken.TOK_ASSIGN) 459 | throw ntok.unexpectedToken("="); 460 | 461 | getExecUnit().initAssigneeVarName(tok.getValue()); 462 | } 463 | 464 | 465 | private boolean parse() throws ScriptParserException { 466 | 467 | DDSToken tok; 468 | 469 | node = this.tokens.listIterator(); 470 | 471 | while (node.hasNext()) { 472 | tok = node.next(); 473 | 474 | if (tok.getType() == DDSToken.TOK_IDENTIFIER) { 475 | handleAssignee(tok); 476 | } 477 | else if (tok.getType() == DDSToken.TOK_BUILTIN) { 478 | handleBuiltinCall(tok); 479 | } 480 | 481 | } 482 | 483 | 484 | return true; 485 | } 486 | 487 | public boolean start(String script) throws ScriptParserException { 488 | 489 | if (!tokenize(script)) 490 | return false; 491 | 492 | if (!parse()) 493 | return false; 494 | 495 | return true; 496 | } 497 | 498 | public void discard() { 499 | tokens.clear(); 500 | callingStack.clear(); 501 | } 502 | } 503 | -------------------------------------------------------------------------------- /src/main/java/dragondance/scripting/DragonDanceScripting.java: -------------------------------------------------------------------------------- 1 | package dragondance.scripting; 2 | 3 | import java.lang.reflect.InvocationTargetException; 4 | import java.util.ArrayList; 5 | import java.util.HashMap; 6 | import java.util.List; 7 | 8 | import dragondance.Log; 9 | import dragondance.components.GuiAffectedOpInterface; 10 | import dragondance.datasource.CoverageData; 11 | import dragondance.eng.DragonHelper; 12 | import dragondance.exceptions.ScriptParserException; 13 | import dragondance.scripting.functions.BuiltinAlias; 14 | import dragondance.scripting.functions.BuiltinFunctionBase; 15 | import dragondance.scripting.functions.impl.BuiltinFunctionClear; 16 | import dragondance.scripting.functions.impl.BuiltinFunctionCwd; 17 | import dragondance.scripting.functions.impl.BuiltinFunctionDiff; 18 | import dragondance.scripting.functions.impl.BuiltinFunctionDiscard; 19 | import dragondance.scripting.functions.impl.BuiltinFunctionDistinct; 20 | import dragondance.scripting.functions.impl.BuiltinFunctionGoto; 21 | import dragondance.scripting.functions.impl.BuiltinFunctionImport; 22 | import dragondance.scripting.functions.impl.BuiltinFunctionIntersect; 23 | import dragondance.scripting.functions.impl.BuiltinFunctionShow; 24 | import dragondance.scripting.functions.impl.BuiltinFunctionSum; 25 | import dragondance.util.Util; 26 | 27 | 28 | public class DragonDanceScripting { 29 | public final static HashMap> builtinFunctions; 30 | public static List executionUnits; 31 | public static HashMap variables; 32 | public static String workingDirectory; 33 | public static String scriptHash = ""; 34 | 35 | static { 36 | builtinFunctions = new HashMap>(); 37 | executionUnits = new ArrayList(); 38 | variables = new HashMap(); 39 | 40 | registerBuiltin("intersect", BuiltinFunctionIntersect.class); 41 | registerBuiltin("diff",BuiltinFunctionDiff.class); 42 | registerBuiltin("sum",BuiltinFunctionSum.class); 43 | registerBuiltin("distinct",BuiltinFunctionDistinct.class); 44 | registerBuiltin("import",BuiltinFunctionImport.class); 45 | registerBuiltin("cwd",BuiltinFunctionCwd.class); 46 | registerBuiltin("show",BuiltinFunctionShow.class); 47 | registerBuiltin("discard",BuiltinFunctionDiscard.class); 48 | registerBuiltin("goto",BuiltinFunctionGoto.class); 49 | registerBuiltin("clear",BuiltinFunctionClear.class); 50 | } 51 | 52 | private static void discardExecutionUnits() { 53 | for (ScriptExecutionUnit seu : executionUnits) 54 | seu.discard(); 55 | 56 | executionUnits.clear(); 57 | 58 | scriptHash = ""; 59 | } 60 | 61 | private static void registerBuiltin(String name, Class clazz) { 62 | 63 | BuiltinAlias aliases = clazz.getAnnotation(BuiltinAlias.class); 64 | 65 | builtinFunctions.put(name, clazz); 66 | 67 | if (aliases != null) { 68 | for (String alias : aliases.aliases()) { 69 | builtinFunctions.put(alias,clazz); 70 | } 71 | } 72 | } 73 | 74 | public static boolean isBuiltin(String str) { 75 | return builtinFunctions.containsKey(str.toLowerCase()); 76 | } 77 | 78 | public static BuiltinFunctionBase newInstance(String func, GuiAffectedOpInterface guisvc) { 79 | BuiltinFunctionBase funcImpl; 80 | Class clazz = null; 81 | 82 | func = func.toLowerCase(); 83 | 84 | if (!builtinFunctions.containsKey(func)) { 85 | return null; 86 | } 87 | 88 | clazz = builtinFunctions.get(func); 89 | 90 | try { 91 | funcImpl = (BuiltinFunctionBase)clazz.getDeclaredConstructor().newInstance(); 92 | } catch (InstantiationException | IllegalAccessException | IllegalArgumentException | InvocationTargetException 93 | | NoSuchMethodException | SecurityException e) { 94 | Log.println("type activation error: %s",e.getMessage()); 95 | return null; 96 | } 97 | 98 | funcImpl.setGuiApi(guisvc); 99 | 100 | return funcImpl; 101 | } 102 | 103 | public static void addExecutionUnit(ScriptExecutionUnit unit) { 104 | executionUnits.add(unit); 105 | } 106 | 107 | public static void removeVariable(ScriptVariable var) { 108 | variables.remove(var.getName().toLowerCase()); 109 | } 110 | 111 | public static void removeVariableByName(String name) { 112 | name = name.toLowerCase(); 113 | 114 | if (!variables.containsKey(name)) 115 | return; 116 | 117 | variables.remove(name); 118 | } 119 | 120 | public static boolean addVariable(ScriptVariable var) { 121 | if (variables.containsKey(var.getName().toLowerCase())) 122 | return false; 123 | 124 | variables.put(var.getName().toLowerCase(), var); 125 | return true; 126 | } 127 | 128 | public static boolean isVariableDeclared(String varName) { 129 | return variables.containsKey(varName.toLowerCase()); 130 | } 131 | 132 | public static ScriptVariable getVariable(String varName) { 133 | if (!isVariableDeclared(varName)) 134 | return null; 135 | 136 | return variables.get(varName.toLowerCase()); 137 | } 138 | 139 | public static void setGuiAffectedInterface(GuiAffectedOpInterface gai) { 140 | DragonDanceScriptParser.setGuiSvc(gai); 141 | } 142 | 143 | public static void discardScriptingSession(boolean discardVariablesAlso) { 144 | discardExecutionUnits(); 145 | 146 | if (discardVariablesAlso) { 147 | Object[] vars = variables.values().toArray(); 148 | 149 | //We don't need to clear variables map. cuz discard method removes 150 | //the scriptvariable from the variable map 151 | 152 | for (Object var : vars) 153 | ((ScriptVariable)var).discard(); 154 | 155 | } 156 | } 157 | 158 | public static boolean execute(String script) { 159 | 160 | boolean result = true; 161 | String shash = Util.md5(script); 162 | 163 | if (!shash.equals(scriptHash)) { 164 | 165 | discardExecutionUnits(); 166 | 167 | DragonDanceScriptParser parser = new DragonDanceScriptParser(); 168 | 169 | try { 170 | parser.start(script); 171 | } catch (ScriptParserException e1) { 172 | DragonHelper.showWarning(e1.getMessage()); 173 | return false; 174 | } 175 | 176 | scriptHash = shash; 177 | } 178 | 179 | try { 180 | for (ScriptExecutionUnit execUnit : executionUnits) { 181 | if (!execUnit.execute()) { 182 | result = false; 183 | break; 184 | } 185 | } 186 | } 187 | catch (Exception e) { 188 | 189 | DragonHelper.showWarning(e.getMessage()); 190 | 191 | discardExecutionUnits(); 192 | 193 | return false; 194 | } 195 | 196 | return result; 197 | } 198 | 199 | public static void setWorkingDirectory(String dir) { 200 | workingDirectory = dir; 201 | } 202 | 203 | public static void removeCoverage(CoverageData cov) { 204 | 205 | if (cov.isLogicalCoverageData()) 206 | return; 207 | 208 | DragonDanceScriptParser.getGAI().removeCoverage(cov.getSourceId()); 209 | } 210 | 211 | } 212 | -------------------------------------------------------------------------------- /src/main/java/dragondance/scripting/ScriptExecutionUnit.java: -------------------------------------------------------------------------------- 1 | package dragondance.scripting; 2 | 3 | import dragondance.components.GuiAffectedOpInterface; 4 | import dragondance.exceptions.ScriptParserException; 5 | import dragondance.scripting.functions.BuiltinArg; 6 | import dragondance.scripting.functions.BuiltinFunctionBase; 7 | 8 | 9 | public class ScriptExecutionUnit { 10 | private ScriptVariable assigneeVar; 11 | private BuiltinFunctionBase function; 12 | 13 | public void initAssigneeVarName(String name) throws ScriptParserException { 14 | if (DragonDanceScripting.isVariableDeclared(name)) { 15 | this.assigneeVar = DragonDanceScripting.getVariable(name); 16 | } 17 | else { 18 | this.assigneeVar = new ScriptVariable(name); 19 | } 20 | } 21 | 22 | public void initFunction(String builtinName, GuiAffectedOpInterface gai) { 23 | this.function = DragonDanceScripting.newInstance(builtinName, gai); 24 | } 25 | 26 | public void pushArgument(BuiltinArg arg) { 27 | this.function.putArg(arg); 28 | } 29 | 30 | public boolean hasAssignee() { 31 | return this.assigneeVar != null; 32 | } 33 | 34 | public boolean hasFunction() { 35 | return this.function != null; 36 | } 37 | 38 | public boolean isItExpectFunction() { 39 | return hasAssignee() && !hasFunction(); 40 | } 41 | 42 | public boolean isCompleted() { 43 | return hasAssignee() && hasFunction(); 44 | } 45 | 46 | public BuiltinFunctionBase getFunction() { 47 | return this.function; 48 | } 49 | 50 | public boolean execute() { 51 | 52 | if (this.function.execute() == null) { 53 | 54 | if (!this.function.hasReturnType()) 55 | return true; 56 | 57 | return false; 58 | } 59 | 60 | if (hasAssignee()) 61 | this.assigneeVar.setResultCoverage(this.function.getReturn()); 62 | 63 | return true; 64 | } 65 | 66 | public void discard() { 67 | this.function.discard(); 68 | this.function = null; 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /src/main/java/dragondance/scripting/ScriptVariable.java: -------------------------------------------------------------------------------- 1 | package dragondance.scripting; 2 | 3 | import dragondance.datasource.CoverageData; 4 | import dragondance.exceptions.ScriptParserException; 5 | 6 | public class ScriptVariable { 7 | 8 | private String name; 9 | private CoverageData coverageValue; 10 | 11 | public ScriptVariable(String name) throws ScriptParserException { 12 | this.name = name; 13 | 14 | if (!DragonDanceScripting.addVariable(this)) 15 | throw new ScriptParserException("%s already declared",name); 16 | } 17 | 18 | public void setResultCoverage(CoverageData coverage) { 19 | 20 | if (this.coverageValue != null) { 21 | if (this.coverageValue.isLogicalCoverageData()) { 22 | this.coverageValue.closeNothrow(); 23 | } 24 | } 25 | 26 | this.coverageValue = coverage; 27 | } 28 | 29 | public final String getName() { 30 | return this.name; 31 | } 32 | 33 | public CoverageData getValue() { 34 | return this.coverageValue; 35 | } 36 | 37 | @Override 38 | public String toString() { 39 | return this.name; 40 | } 41 | 42 | public final boolean isNull() { 43 | return this.coverageValue == null; 44 | } 45 | 46 | public void discard(boolean forceDeletePhysicalCoverage) { 47 | //dispose only logical result coverage object. 48 | //dont touch the physical coverage data 49 | 50 | if (this.coverageValue != null) { 51 | if (this.coverageValue.isLogicalCoverageData()) 52 | this.coverageValue.closeNothrow(); 53 | else if (forceDeletePhysicalCoverage) { 54 | DragonDanceScripting.removeCoverage(this.coverageValue); 55 | } 56 | } 57 | 58 | //but the holder variable can be delete in any case 59 | DragonDanceScripting.removeVariable(this); 60 | } 61 | 62 | public void discard() { 63 | discard(false); 64 | } 65 | } -------------------------------------------------------------------------------- /src/main/java/dragondance/scripting/functions/BuiltinAlias.java: -------------------------------------------------------------------------------- 1 | package dragondance.scripting.functions; 2 | 3 | import static java.lang.annotation.RetentionPolicy.RUNTIME; 4 | 5 | import java.lang.annotation.Retention; 6 | 7 | @Retention(RUNTIME) 8 | public @interface BuiltinAlias { 9 | String[] aliases(); 10 | } 11 | -------------------------------------------------------------------------------- /src/main/java/dragondance/scripting/functions/BuiltinArg.java: -------------------------------------------------------------------------------- 1 | package dragondance.scripting.functions; 2 | 3 | import dragondance.scripting.ScriptVariable; 4 | 5 | public class BuiltinArg { 6 | 7 | private Object argContainer; 8 | 9 | public BuiltinArg(ScriptVariable var) { 10 | this.argContainer=var; 11 | } 12 | 13 | public BuiltinArg(BuiltinFunctionBase func) { 14 | this.argContainer = func; 15 | } 16 | 17 | public BuiltinArg(String sarg) { 18 | this.argContainer = sarg; 19 | } 20 | 21 | public BuiltinArg(long larg) { 22 | this.argContainer = larg; 23 | } 24 | 25 | public boolean isBuiltinCall() { 26 | return this.argContainer instanceof BuiltinFunctionBase; 27 | } 28 | 29 | public boolean isVariable() { 30 | return this.argContainer instanceof ScriptVariable; 31 | } 32 | 33 | public boolean isString() { 34 | return this.argContainer instanceof String; 35 | } 36 | 37 | public boolean isInteger() { 38 | return this.argContainer instanceof Long; 39 | } 40 | 41 | public BuiltinFunctionBase getAsFunction() { 42 | return (BuiltinFunctionBase)this.argContainer; 43 | } 44 | 45 | public ScriptVariable getAsVariable() { 46 | return (ScriptVariable)this.argContainer; 47 | } 48 | 49 | public String getAsString() { 50 | return (String)this.argContainer; 51 | } 52 | 53 | public long getAsLong() { 54 | return ((Long)this.argContainer).longValue(); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/main/java/dragondance/scripting/functions/BuiltinFunctionBase.java: -------------------------------------------------------------------------------- 1 | package dragondance.scripting.functions; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | 6 | import dragondance.Log; 7 | import dragondance.components.GuiAffectedOpInterface; 8 | import dragondance.datasource.CoverageData; 9 | 10 | public abstract class BuiltinFunctionBase { 11 | private String name; 12 | private List aliases; 13 | 14 | private List args; 15 | private CoverageData retVal; 16 | 17 | protected GuiAffectedOpInterface guiSvc; 18 | 19 | public BuiltinFunctionBase(String name) { 20 | this.name = name; 21 | this.args = new ArrayList(); 22 | } 23 | 24 | public void putArg(BuiltinArg arg1, BuiltinArg arg2) { 25 | this.args.add(arg1); 26 | this.args.add(arg2); 27 | } 28 | 29 | public void putArg(BuiltinArg ...fargs) { 30 | for (BuiltinArg arg : fargs) 31 | this.args.add(arg); 32 | } 33 | 34 | public CoverageData getReturn() { 35 | return this.retVal; 36 | } 37 | 38 | public int argCount() { 39 | return this.args.size(); 40 | } 41 | 42 | protected void addAlias(String alias) { 43 | if (this.aliases == null) 44 | this.aliases = new ArrayList(); 45 | 46 | this.aliases.add(alias); 47 | } 48 | 49 | protected void setReturn(CoverageData cov) { 50 | this.retVal = cov; 51 | } 52 | 53 | protected CoverageData[] prepareFinalArguments() { 54 | CoverageData[] covArgs = new CoverageData[this.args.size()]; 55 | int index=0; 56 | 57 | for (BuiltinArg arg : this.args) { 58 | if (arg.isBuiltinCall()) { 59 | covArgs[index++] = arg.getAsFunction().execute(); 60 | } 61 | else if (arg.isVariable()) { 62 | covArgs[index++] = arg.getAsVariable().getValue(); 63 | } 64 | } 65 | 66 | return covArgs; 67 | } 68 | 69 | protected Object[] prepareArguments() { 70 | Object[] sargs = new Object[this.args.size()]; 71 | int index=0; 72 | 73 | for (BuiltinArg arg : this.args) { 74 | if (arg.isBuiltinCall()) { 75 | sargs[index++] = arg.getAsFunction().execute(); 76 | } 77 | else if (arg.isVariable()) { 78 | sargs[index++] = arg.getAsVariable(); 79 | } 80 | else if (arg.isInteger()) { 81 | sargs[index++] = arg.getAsLong(); 82 | } 83 | else 84 | sargs[index++] =arg.getAsString(); 85 | 86 | } 87 | 88 | return sargs; 89 | } 90 | 91 | protected String[] getStringArguments() { 92 | String[] strArgs = new String[this.args.size()]; 93 | int index=0; 94 | 95 | for (BuiltinArg arg : this.args) { 96 | strArgs[index++] = arg.getAsString(); 97 | } 98 | 99 | return strArgs; 100 | } 101 | 102 | public List getAliases() { 103 | return this.aliases; 104 | } 105 | 106 | public boolean hasAlias() { 107 | return this.aliases != null; 108 | } 109 | 110 | public void setGuiApi(GuiAffectedOpInterface guiSvc) { 111 | this.guiSvc = guiSvc; 112 | } 113 | 114 | public int requiredArgCount(boolean minimum) { 115 | return 0; 116 | } 117 | 118 | public boolean hasReturnType() { 119 | return true; 120 | } 121 | 122 | public CoverageData execute() { 123 | Log.debug("builtin \"%s\" executed", this.name); 124 | return this.retVal; 125 | } 126 | 127 | public void discard() { 128 | if (this.aliases != null) 129 | this.aliases.clear(); 130 | 131 | this.args.clear(); 132 | } 133 | } 134 | -------------------------------------------------------------------------------- /src/main/java/dragondance/scripting/functions/impl/BuiltinFunctionClear.java: -------------------------------------------------------------------------------- 1 | package dragondance.scripting.functions.impl; 2 | 3 | import dragondance.datasource.CoverageData; 4 | import dragondance.scripting.functions.BuiltinFunctionBase; 5 | 6 | public class BuiltinFunctionClear extends BuiltinFunctionBase { 7 | 8 | public BuiltinFunctionClear() { 9 | super("clear"); 10 | } 11 | 12 | @Override 13 | public int requiredArgCount(boolean minimum) { 14 | return 0; 15 | } 16 | 17 | @Override 18 | public boolean hasReturnType() { 19 | return false; 20 | } 21 | 22 | @Override 23 | public CoverageData execute() { 24 | guiSvc.visualizeCoverage(null); 25 | return super.execute(); 26 | } 27 | 28 | } 29 | -------------------------------------------------------------------------------- /src/main/java/dragondance/scripting/functions/impl/BuiltinFunctionCwd.java: -------------------------------------------------------------------------------- 1 | package dragondance.scripting.functions.impl; 2 | 3 | import dragondance.datasource.CoverageData; 4 | import dragondance.scripting.DragonDanceScripting; 5 | import dragondance.scripting.functions.BuiltinFunctionBase; 6 | 7 | public class BuiltinFunctionCwd extends BuiltinFunctionBase { 8 | 9 | public BuiltinFunctionCwd() { 10 | super("cwd"); 11 | } 12 | 13 | @Override 14 | public int requiredArgCount(boolean minimum) { 15 | return 1; 16 | } 17 | 18 | @Override 19 | public boolean hasReturnType() { 20 | return false; 21 | } 22 | 23 | @Override 24 | public CoverageData execute() { 25 | String[] finalArgs = getStringArguments(); 26 | 27 | DragonDanceScripting.setWorkingDirectory(finalArgs[0]); 28 | 29 | return super.execute(); 30 | } 31 | 32 | } 33 | -------------------------------------------------------------------------------- /src/main/java/dragondance/scripting/functions/impl/BuiltinFunctionDiff.java: -------------------------------------------------------------------------------- 1 | package dragondance.scripting.functions.impl; 2 | 3 | import dragondance.datasource.CoverageData; 4 | import dragondance.scripting.functions.BuiltinFunctionBase; 5 | 6 | 7 | public class BuiltinFunctionDiff extends BuiltinFunctionBase { 8 | 9 | public BuiltinFunctionDiff() { 10 | super("diff"); 11 | } 12 | 13 | @Override 14 | public int requiredArgCount(boolean minimum) { 15 | if (minimum) 16 | return 2; 17 | 18 | return -1; 19 | } 20 | 21 | @Override 22 | public CoverageData execute() { 23 | CoverageData[] finalArgs = prepareFinalArguments(); 24 | 25 | setReturn(CoverageData.difference(finalArgs)); 26 | 27 | return super.execute(); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/main/java/dragondance/scripting/functions/impl/BuiltinFunctionDiscard.java: -------------------------------------------------------------------------------- 1 | package dragondance.scripting.functions.impl; 2 | 3 | import dragondance.datasource.CoverageData; 4 | import dragondance.exceptions.DragonDanceScriptRuntimeException; 5 | import dragondance.scripting.ScriptVariable; 6 | import dragondance.scripting.functions.BuiltinAlias; 7 | import dragondance.scripting.functions.BuiltinFunctionBase; 8 | 9 | @BuiltinAlias(aliases = {"del"}) 10 | public class BuiltinFunctionDiscard extends BuiltinFunctionBase { 11 | 12 | public BuiltinFunctionDiscard() { 13 | super("discard"); 14 | } 15 | 16 | @Override 17 | public int requiredArgCount(boolean minimum) { 18 | if (minimum) 19 | return 1; 20 | 21 | return -1; 22 | } 23 | 24 | @Override 25 | public boolean hasReturnType() { 26 | return false; 27 | } 28 | 29 | @Override 30 | public CoverageData execute() { 31 | Object[] finalArgs = prepareArguments(); 32 | 33 | for (Object arg : finalArgs) { 34 | if (arg instanceof CoverageData) { 35 | ((CoverageData)arg).closeNothrow(); 36 | } 37 | else if (arg instanceof ScriptVariable) { 38 | ((ScriptVariable)arg).discard(true); 39 | } 40 | else 41 | throw new DragonDanceScriptRuntimeException("invalid arg type for builtin"); 42 | 43 | } 44 | 45 | 46 | return super.execute(); 47 | } 48 | 49 | 50 | } 51 | -------------------------------------------------------------------------------- /src/main/java/dragondance/scripting/functions/impl/BuiltinFunctionDistinct.java: -------------------------------------------------------------------------------- 1 | package dragondance.scripting.functions.impl; 2 | 3 | import dragondance.datasource.CoverageData; 4 | import dragondance.scripting.functions.BuiltinAlias; 5 | import dragondance.scripting.functions.BuiltinFunctionBase; 6 | 7 | @BuiltinAlias(aliases = {"xor"}) 8 | public class BuiltinFunctionDistinct extends BuiltinFunctionBase { 9 | 10 | public BuiltinFunctionDistinct() { 11 | super("distinct"); 12 | } 13 | 14 | @Override 15 | public int requiredArgCount(boolean minimum) { 16 | if (minimum) 17 | return 2; 18 | 19 | return -1; 20 | } 21 | 22 | @Override 23 | public CoverageData execute() { 24 | CoverageData[] finalArgs = prepareFinalArguments(); 25 | 26 | setReturn(CoverageData.distinct(finalArgs)); 27 | 28 | return super.execute(); 29 | } 30 | 31 | 32 | } 33 | -------------------------------------------------------------------------------- /src/main/java/dragondance/scripting/functions/impl/BuiltinFunctionGoto.java: -------------------------------------------------------------------------------- 1 | package dragondance.scripting.functions.impl; 2 | 3 | import dragondance.datasource.CoverageData; 4 | import dragondance.exceptions.DragonDanceScriptRuntimeException; 5 | import dragondance.scripting.functions.BuiltinFunctionBase; 6 | 7 | public class BuiltinFunctionGoto extends BuiltinFunctionBase { 8 | 9 | public BuiltinFunctionGoto() { 10 | super("goto"); 11 | } 12 | 13 | @Override 14 | public int requiredArgCount(boolean minimum) { 15 | return 1; 16 | } 17 | 18 | @Override 19 | public boolean hasReturnType() { 20 | return false; 21 | } 22 | 23 | @Override 24 | public CoverageData execute() { 25 | Object arg = super.prepareArguments()[0]; 26 | 27 | if (arg instanceof Long) { 28 | guiSvc.goTo(((Long)arg).longValue()); 29 | } 30 | else 31 | throw new DragonDanceScriptRuntimeException("invalid arg type for goto"); 32 | 33 | return super.execute(); 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /src/main/java/dragondance/scripting/functions/impl/BuiltinFunctionImport.java: -------------------------------------------------------------------------------- 1 | package dragondance.scripting.functions.impl; 2 | 3 | import java.io.File; 4 | import java.io.FileNotFoundException; 5 | import java.nio.file.Paths; 6 | import dragondance.datasource.CoverageData; 7 | import dragondance.eng.session.Session; 8 | import dragondance.eng.session.SessionManager; 9 | import dragondance.exceptions.DragonDanceScriptRuntimeException; 10 | import dragondance.scripting.DragonDanceScripting; 11 | import dragondance.scripting.functions.BuiltinAlias; 12 | import dragondance.scripting.functions.BuiltinFunctionBase; 13 | 14 | 15 | @BuiltinAlias(aliases = { "load","get" }) 16 | public class BuiltinFunctionImport extends BuiltinFunctionBase { 17 | 18 | public BuiltinFunctionImport() { 19 | super("import"); 20 | } 21 | 22 | @Override 23 | public int requiredArgCount(boolean minimum) { 24 | return 1; 25 | } 26 | 27 | 28 | private String prepareFilePath(String inpFile) { 29 | File file = new File(inpFile); 30 | 31 | if (file.exists()) 32 | return inpFile; 33 | 34 | if (file.isAbsolute()) 35 | return inpFile; 36 | 37 | if (DragonDanceScripting.workingDirectory == null) 38 | return inpFile; 39 | 40 | return Paths.get(DragonDanceScripting.workingDirectory, inpFile).toString(); 41 | } 42 | 43 | private CoverageData loadCoverage(String fileOrName) throws FileNotFoundException { 44 | String prepFile; 45 | CoverageData coverage=null; 46 | Session session = SessionManager.getActiveSession(); 47 | 48 | if (session == null) 49 | return null; 50 | 51 | if (fileOrName.contains(File.separator) | fileOrName.contains(".")) { 52 | prepFile = prepareFilePath(fileOrName); 53 | 54 | coverage = session.tryGetPreviouslyLoadedCoverage(prepFile); 55 | 56 | if (coverage != null) 57 | return coverage; 58 | 59 | return guiSvc.loadCoverage(prepFile); 60 | } 61 | 62 | return session.getCoverageByName(fileOrName); 63 | } 64 | 65 | @Override 66 | public CoverageData execute() { 67 | String[] finalArgs = getStringArguments(); 68 | 69 | CoverageData coverage; 70 | 71 | try { 72 | coverage = loadCoverage(finalArgs[0]); 73 | } catch (FileNotFoundException e) { 74 | throw new DragonDanceScriptRuntimeException(String.format("\"%s\" not found", finalArgs[0])); 75 | } 76 | 77 | setReturn(coverage); 78 | 79 | return super.execute(); 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /src/main/java/dragondance/scripting/functions/impl/BuiltinFunctionIntersect.java: -------------------------------------------------------------------------------- 1 | package dragondance.scripting.functions.impl; 2 | 3 | import dragondance.datasource.CoverageData; 4 | import dragondance.scripting.functions.BuiltinAlias; 5 | import dragondance.scripting.functions.BuiltinFunctionBase; 6 | 7 | @BuiltinAlias(aliases = {"and"}) 8 | public class BuiltinFunctionIntersect extends BuiltinFunctionBase { 9 | 10 | public BuiltinFunctionIntersect() { 11 | super("intersect"); 12 | } 13 | 14 | @Override 15 | public int requiredArgCount(boolean minimum) { 16 | if (minimum) 17 | return 2; 18 | 19 | return -1; 20 | } 21 | 22 | @Override 23 | public CoverageData execute() { 24 | CoverageData[] finalArgs = prepareFinalArguments(); 25 | 26 | setReturn(CoverageData.intersect(finalArgs)); 27 | 28 | return super.execute(); 29 | } 30 | 31 | } 32 | -------------------------------------------------------------------------------- /src/main/java/dragondance/scripting/functions/impl/BuiltinFunctionShow.java: -------------------------------------------------------------------------------- 1 | package dragondance.scripting.functions.impl; 2 | 3 | import dragondance.datasource.CoverageData; 4 | import dragondance.scripting.functions.BuiltinFunctionBase; 5 | 6 | public class BuiltinFunctionShow extends BuiltinFunctionBase { 7 | 8 | public BuiltinFunctionShow() { 9 | super("show"); 10 | } 11 | 12 | @Override 13 | public int requiredArgCount(boolean minimum) { 14 | return 1; 15 | } 16 | 17 | @Override 18 | public boolean hasReturnType() { 19 | return false; 20 | } 21 | 22 | @Override 23 | public CoverageData execute() { 24 | CoverageData[] finalArgs = prepareFinalArguments(); 25 | 26 | guiSvc.visualizeCoverage(finalArgs[0]); 27 | 28 | return super.execute(); 29 | } 30 | 31 | } 32 | -------------------------------------------------------------------------------- /src/main/java/dragondance/scripting/functions/impl/BuiltinFunctionSum.java: -------------------------------------------------------------------------------- 1 | package dragondance.scripting.functions.impl; 2 | 3 | import dragondance.datasource.CoverageData; 4 | import dragondance.scripting.functions.BuiltinAlias; 5 | import dragondance.scripting.functions.BuiltinFunctionBase; 6 | 7 | @BuiltinAlias(aliases = {"or","union"}) 8 | public class BuiltinFunctionSum extends BuiltinFunctionBase { 9 | 10 | public BuiltinFunctionSum() { 11 | super("sum"); 12 | } 13 | 14 | @Override 15 | public int requiredArgCount(boolean minimum) { 16 | if (minimum) 17 | return 2; 18 | 19 | return -1; 20 | } 21 | 22 | @Override 23 | public CoverageData execute() { 24 | CoverageData[] finalArgs = prepareFinalArguments(); 25 | 26 | setReturn(CoverageData.sum(finalArgs)); 27 | 28 | return super.execute(); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/dragondance/util/TextGraphic.java: -------------------------------------------------------------------------------- 1 | package dragondance.util; 2 | 3 | import java.awt.Color; 4 | import java.awt.Font; 5 | import java.awt.FontMetrics; 6 | import java.awt.Graphics; 7 | import java.awt.Point; 8 | import java.awt.geom.Rectangle2D; 9 | import java.awt.image.BufferedImage; 10 | import java.util.ArrayList; 11 | import java.util.List; 12 | import java.util.Stack; 13 | 14 | 15 | public class TextGraphic { 16 | 17 | class RenderItem { 18 | public static final int RI_TEXTOUT=0; 19 | public static final int RI_INCRPOS=1; 20 | public static final int RI_PUSHPOPFONT=2; 21 | public static final int RI_PUSHPOPCOLOR=3; 22 | public static final int RI_NEWLINE=4; 23 | public static final int RI_FLOATTEXTBLOCK=5; 24 | 25 | private int type; 26 | private Object[] v; 27 | 28 | public RenderItem(int t, Object...args) { 29 | this.type=t; 30 | this.v = args; 31 | } 32 | } 33 | 34 | private int w,h; 35 | private BufferedImage img; 36 | private Graphics gph; 37 | 38 | private Point pos; 39 | private int lastStringHeight=0; 40 | private List items; 41 | private Stack fontStack; 42 | private Stack colorStack; 43 | private int textMaxWidth=0,textMaxHeight=0; 44 | private Font currFont; 45 | private Color currColor; 46 | private Color backgroundColor; 47 | private Point virtCoord; 48 | 49 | public TextGraphic(int width, int height, Color bgColor) { 50 | this.w = width; 51 | this.h = height; 52 | 53 | this.backgroundColor = bgColor; 54 | 55 | this.img = new BufferedImage(w, h, BufferedImage.TYPE_INT_RGB); 56 | this.gph = this.img.getGraphics(); 57 | 58 | this.items = new ArrayList(); 59 | this.fontStack = new Stack(); 60 | this.colorStack = new Stack(); 61 | 62 | this.pos = new Point(0,0); 63 | 64 | this.virtCoord = new Point(0,this.gph.getFontMetrics().getAscent()); 65 | 66 | this.currColor = this.gph.getColor(); 67 | this.currFont = this.gph.getFont(); 68 | 69 | clearScene(); 70 | } 71 | 72 | private Point logicalToPhysical(Point p) { 73 | Point np = new Point(); 74 | 75 | np.x = this.virtCoord.x + p.x; 76 | np.y = this.virtCoord.y + p.y; 77 | 78 | return np; 79 | } 80 | 81 | private TextGraphic newRender(int type, Object...args) { 82 | items.add(new RenderItem(type,args)); 83 | return this; 84 | } 85 | 86 | public TextGraphic textOut(String format, Color color, Object ...args) { 87 | return textOut(String.format(format, args),color); 88 | } 89 | 90 | public TextGraphic textOut(String s, Color color) { 91 | return newRender(RenderItem.RI_TEXTOUT,s,color); 92 | } 93 | 94 | public TextGraphic incrementPos(int x, int y) { 95 | return newRender(RenderItem.RI_INCRPOS,x,y); 96 | } 97 | 98 | public TextGraphic decrementPos(int x, int y) { 99 | return incrementPos(-x,-y); 100 | } 101 | 102 | public TextGraphic pushFont(String fontName, int size, int style) { 103 | return newRender(RenderItem.RI_PUSHPOPFONT,fontName,size,style); 104 | } 105 | 106 | public TextGraphic popFont() { 107 | return newRender(RenderItem.RI_PUSHPOPFONT); 108 | } 109 | 110 | public TextGraphic pushColor(Color color) { 111 | return newRender(RenderItem.RI_PUSHPOPCOLOR,color); 112 | } 113 | 114 | public TextGraphic popColor() { 115 | return newRender(RenderItem.RI_PUSHPOPCOLOR); 116 | } 117 | 118 | public TextGraphic newLine() { 119 | return newRender(RenderItem.RI_NEWLINE); 120 | } 121 | 122 | public TextGraphic floatTextBlock() { 123 | return newRender(RenderItem.RI_FLOATTEXTBLOCK); 124 | } 125 | 126 | 127 | private void clearScene() { 128 | Color prev = this.gph.getColor(); 129 | this.gph.setColor(this.backgroundColor); 130 | this.gph.fillRect(0, 0, this.w,this.h); 131 | 132 | /* 133 | this.gph.setColor(Color.BLACK); 134 | 135 | 136 | this.gph.drawLine(0, 0, this.w, 0); //top line 137 | this.gph.drawLine(0, 0, 0, this.h); //left line 138 | this.gph.drawLine(0, this.h-1, this.w-1, this.h-1); //bottom line 139 | this.gph.drawLine(this.w-1, 0, this.w-1,this.h-1); //right line 140 | */ 141 | 142 | this.gph.setColor(prev); 143 | 144 | 145 | } 146 | 147 | private void renderText(RenderItem r) { 148 | String s; 149 | Color prev,color; 150 | 151 | s = (String)r.v[0]; 152 | color = (Color)r.v[1]; 153 | 154 | prev = this.gph.getColor(); 155 | 156 | FontMetrics fm = this.gph.getFontMetrics(); 157 | Rectangle2D rect = fm.getStringBounds(s, this.gph); 158 | 159 | this.gph.setColor(color); 160 | 161 | Point rpos = logicalToPhysical(this.pos); 162 | 163 | 164 | this.gph.drawString(s, rpos.x, rpos.y); 165 | this.gph.setColor(prev); 166 | 167 | 168 | this.lastStringHeight = (int)rect.getHeight(); 169 | 170 | if (this.pos.x + rect.getWidth() > this.textMaxWidth) 171 | this.textMaxWidth += this.pos.x + (int)rect.getWidth(); 172 | 173 | if (this.pos.y + rect.getHeight() > this.textMaxHeight) 174 | this.textMaxHeight += this.pos.y + (int)rect.getHeight(); 175 | 176 | this.pos.x += (int)rect.getWidth(); 177 | } 178 | 179 | private void incrPos(RenderItem r) { 180 | int x,y; 181 | 182 | x = ((Integer)r.v[0]).intValue(); 183 | y = ((Integer)r.v[1]).intValue(); 184 | 185 | this.pos.x += x; 186 | this.pos.y += y; 187 | } 188 | 189 | private void pushPopFont(RenderItem r) { 190 | String fontName; 191 | int fontSize,style; 192 | 193 | if (r.v.length > 0) { 194 | fontName = (String)r.v[0]; 195 | fontSize = ((Integer)r.v[1]).intValue(); 196 | style = ((Integer)r.v[2]).intValue(); 197 | 198 | Font f = new Font(fontName,style,fontSize); 199 | fontStack.push(currFont); 200 | this.currFont = f; 201 | } 202 | else { 203 | if (!fontStack.empty()) 204 | this.currFont = fontStack.pop(); 205 | } 206 | 207 | this.gph.setFont(this.currFont); 208 | } 209 | 210 | private void pushPopColor(RenderItem r) { 211 | Color color; 212 | 213 | if (r.v.length > 0) { 214 | color = (Color)r.v[0]; 215 | colorStack.push(this.currColor); 216 | this.currColor=color; 217 | } 218 | else { 219 | if (!colorStack.empty()) 220 | this.currColor = colorStack.pop(); 221 | } 222 | 223 | this.gph.setColor(this.currColor); 224 | } 225 | 226 | private void doNewLine() { 227 | this.pos.x=0; 228 | this.pos.y += this.lastStringHeight; 229 | } 230 | 231 | private void doFloatBlock() { 232 | if (this.pos.x + this.textMaxWidth > this.w - 10) { 233 | //float down the block 234 | this.virtCoord.x = 0; 235 | this.virtCoord.y = this.textMaxHeight; 236 | 237 | } 238 | else { 239 | this.virtCoord.x = this.textMaxWidth; 240 | this.virtCoord.y = this.gph.getFontMetrics().getAscent(); 241 | } 242 | 243 | this.pos.x=0; 244 | this.pos.y=0; 245 | } 246 | 247 | public void clear() { 248 | fontStack.clear(); 249 | colorStack.clear(); 250 | items.clear(); 251 | 252 | this.pos.setLocation(0, 0); 253 | this.virtCoord.setLocation(0, this.gph.getFontMetrics().getAscent()); 254 | 255 | this.textMaxHeight=0; 256 | this.textMaxWidth=0; 257 | 258 | } 259 | 260 | public void render(boolean clearAfter) { 261 | 262 | clearScene(); 263 | 264 | for (RenderItem r : this.items) { 265 | switch (r.type) { 266 | case RenderItem.RI_TEXTOUT: 267 | renderText(r); 268 | break; 269 | case RenderItem.RI_INCRPOS: 270 | incrPos(r); 271 | break; 272 | case RenderItem.RI_PUSHPOPFONT: 273 | pushPopFont(r); 274 | break; 275 | case RenderItem.RI_PUSHPOPCOLOR: 276 | pushPopColor(r); 277 | break; 278 | case RenderItem.RI_NEWLINE: 279 | doNewLine(); 280 | break; 281 | case RenderItem.RI_FLOATTEXTBLOCK: 282 | doFloatBlock(); 283 | break; 284 | default: 285 | break; 286 | } 287 | } 288 | 289 | if (clearAfter) 290 | clear(); 291 | } 292 | 293 | public void dispatch(Graphics g) { 294 | g.drawImage(this.img,0,0,this.w,this.h,null); 295 | } 296 | } 297 | -------------------------------------------------------------------------------- /src/main/java/dragondance/util/Util.java: -------------------------------------------------------------------------------- 1 | package dragondance.util; 2 | 3 | import java.io.File; 4 | import java.security.MessageDigest; 5 | import java.security.NoSuchAlgorithmException; 6 | 7 | import dragondance.Globals; 8 | import dragondance.eng.DragonHelper; 9 | 10 | public final class Util { 11 | public static String getObjectNameFromPath(String path) { 12 | int p1,p2; 13 | 14 | p1 = path.lastIndexOf(File.separator); 15 | p2 = path.lastIndexOf("."); 16 | 17 | if (p1 < 0) { 18 | p1 = 0; 19 | } 20 | 21 | if (p2 < 0) { 22 | p2 = path.length(); 23 | } 24 | 25 | return path.substring(p1+1, p2); 26 | } 27 | 28 | public static String getDirectoryOfFile(String path) { 29 | File file = new File(path); 30 | 31 | if (file.isDirectory()) 32 | return file.getAbsolutePath(); 33 | 34 | return file.getParent(); 35 | } 36 | 37 | private static int parseVersion(String ver) { 38 | 39 | if (ver == null) 40 | return 0; 41 | 42 | String[] parts = ver.trim().split("\\."); 43 | 44 | if (parts.length==0) 45 | return 0; 46 | 47 | int nver=0; 48 | 49 | try { 50 | nver = Integer.parseInt(parts[0]) << 16; 51 | 52 | if (parts.length>1) 53 | nver |= Integer.parseInt(parts[1]) << 8; 54 | 55 | if (parts.length>2) 56 | nver |= Integer.parseInt(parts[2]); 57 | } 58 | catch (NumberFormatException e) { 59 | return 0; 60 | } 61 | 62 | return nver; 63 | } 64 | 65 | public static boolean checkForNewVersion() { 66 | String content = 67 | DragonHelper.getStringFromURL("https://raw.githubusercontent.com/0ffffffffh/dragondance/master/.version"); 68 | 69 | if (content == null) 70 | return false; 71 | 72 | int lver,pver; 73 | 74 | lver = parseVersion(content); 75 | pver = parseVersion(Globals.version); 76 | 77 | if (lver <= pver) 78 | return false; 79 | 80 | return true; 81 | } 82 | 83 | public static String md5(String sval) { 84 | return md5(sval.getBytes()); 85 | } 86 | 87 | public static String md5(byte[] value) { 88 | MessageDigest md = null; 89 | StringBuffer sb = new StringBuffer(); 90 | 91 | try { 92 | md = MessageDigest.getInstance("MD5"); 93 | } catch (NoSuchAlgorithmException e) { 94 | return null; 95 | } 96 | 97 | md.update(value); 98 | byte[] digest = md.digest(); 99 | 100 | for (byte b : digest) { 101 | sb.append(String.format("%02x", b)); 102 | } 103 | 104 | return sb.toString(); 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /src/main/resources/images/README.txt: -------------------------------------------------------------------------------- 1 | The "src/resources/images" directory is intended to hold all image/icon files used by 2 | this module. 3 | --------------------------------------------------------------------------------