Repository: joernio/joern
Pull request #725 (2021-11-12 11:10:51)

Improved literal resolving


Patch:
diff --git a/joern-cli/frontends/ghidra2cpg/src/main/scala/io/joern/ghidra2cpg/Ghidra2Cpg.scala b/joern-cli/frontends/ghidra2cpg/src/main/scala/io/joern/ghidra2cpg/Ghidra2Cpg.scala
index 3684b6297..613419aaf 100644
--- a/joern-cli/frontends/ghidra2cpg/src/main/scala/io/joern/ghidra2cpg/Ghidra2Cpg.scala
+++ b/joern-cli/frontends/ghidra2cpg/src/main/scala/io/joern/ghidra2cpg/Ghidra2Cpg.scala
@@ -19,85 +19,68 @@ import io.joern.ghidra2cpg.passes.mips.{LoHiPass, MipsFunctionPass}
 import io.joern.ghidra2cpg.passes.x86.X86FunctionPass
 import io.shiftleft.passes.KeyPoolCreator
 import io.shiftleft.x2cpg.X2Cpg
-import org.apache.commons.io.FileUtils
 import utilities.util.FileUtilities
 
 import java.io.File
-import java.nio.file.Files
 import scala.collection.mutable
 import scala.jdk.CollectionConverters._
-object Types {
 
-  // Types will be added to the CPG as soon as everything
-  // else is done
-  val types: mutable.SortedSet[String] = scala.collection.mutable.SortedSet[String]()
-  def registerType(typeName: String): String = {
-    types += typeName
-    typeName
-  }
-}
-class Ghidra2Cpg(
-    inputFile: File,
-    outputFile: Option[String]
-) {
+class Ghidra2Cpg() {
 
-  val tempWorkingDir: File = Files.createTempDirectory("ghidra2cpg_tmp").toFile
-  // tempWorkingDir.deleteOnExit() is not reliable,
-  // adding a shutdown hook seems to work https://stackoverflow.com/posts/35212952/revisions
-  Runtime.getRuntime.addShutdownHook(new Thread(() => FileUtils.deleteQuietly(tempWorkingDir)))
+  def createCpg(inputFile: File, outputFile: Option[String]): Unit = {
 
-  def createCpg(): Unit = {
+    if (!inputFile.isDirectory && !inputFile.isFile)
+      throw new InvalidInputException(
+        s"$inputFile is not a valid directory or file."
+      )
+
+    better.files.File.usingTemporaryDirectory("ghidra2cpg_tmp") { tempWorkingDir =>
+      initGhidra()
+      val locator = new ProjectLocator(tempWorkingDir.path.toAbsolutePath.toString, CommandLineConfig.projectName)
+      var program: Program = null
+      var project: Project = null
+
+      try {
+        val projectManager = new HeadlessGhidraProjectManager
+        project = projectManager.createProject(locator, null, false)
+        program = AutoImporter.importByUsingBestGuess(
+          inputFile,
+          null,
+          this,
+          new MessageLog,
+          TaskMonitor.DUMMY
+        )
+
+        analyzeProgram(inputFile.getAbsolutePath, program, outputFile)
+      } catch {
+        case e: Throwable =>
+          e.printStackTrace()
+      } finally {
+        if (program != null) {
+          AutoAnalysisManager.getAnalysisManager(program).dispose()
+          program.release(this)
+        }
+        project.close()
+        FileUtilities.deleteDir(locator.getProjectDir)
+        locator.getMarkerFile.delete
+      }
+    }
+
+  }
+
+  private def initGhidra(): Unit = {
     // We need this for the URL handler
     Handler.registerHandler()
 
-    var projectManager: Option[HeadlessGhidraProjectManager] =
-      None: Option[HeadlessGhidraProjectManager]
-
-    var project: Option[Project] = None
     // Initialize application (if necessary and only once)
     if (!Application.isInitialized) {
       val configuration = new HeadlessGhidraApplicationConfiguration
       configuration.setInitializeLogging(false)
       Application.initializeApplication(new GhidraJarApplicationLayout, configuration)
     }
-
-    if (!inputFile.isDirectory && !inputFile.isFile)
-      throw new InvalidInputException(
-        s"$inputFile is not a valid directory or file."
-      )
-
-    val locator = new ProjectLocator(tempWorkingDir.getAbsolutePath, CommandLineConfig.projectName)
-    var program: Program = null
-    try {
-      projectManager = Some(new HeadlessGhidraProjectManager)
-      project = Some(projectManager.get.createProject(locator, null, false))
-      program = AutoImporter.importByUsingBestGuess(
-        inputFile,
-        null,
-        this,
-        new MessageLog,
-        TaskMonitor.DUMMY
-      )
-
-      analyzeProgram(inputFile.getAbsolutePath, program)
-    } catch {
-      case e: Throwable =>
-        e.printStackTrace()
-    } finally {
-      if (program != null) {
-        AutoAnalysisManager.getAnalysisManager(program).dispose()
-        program.release(this)
-        program = null
-      }
-      project.get.close()
-      // Used to have this in a config but we delete the directory anyway
-      // if (!config.runScriptsNoImport && config.deleteProject)
-      FileUtilities.deleteDir(locator.getProjectDir)
-      locator.getMarkerFile.delete
-
-    }
   }
-  private def analyzeProgram(fileAbsolutePath: String, program: Program): Unit = {
+
+  private def analyzeProgram(fileAbsolutePath: String, program: Program, outputFile: Option[String]): Unit = {
     val autoAnalysisManager: AutoAnalysisManager = AutoAnalysisManager.getAnalysisManager(program)
     val transactionId: Int = program.startTransaction("Analysis")
     try {
@@ -112,14 +95,14 @@ class Ghidra2Cpg(
       program.endTransaction(transactionId, true)
     }
     try {
-      handleProgram(program, fileAbsolutePath)
+      handleProgram(program, fileAbsolutePath, outputFile)
     } catch {
       case e: Throwable =>
         e.printStackTrace()
     }
   }
 
-  def handleProgram(currentProgram: Program, fileAbsolutePath: String): Unit = {
+  def handleProgram(currentProgram: Program, fileAbsolutePath: String, outputFile: Option[String]): Unit = {
 
     val flatProgramAPI: FlatProgramAPI = new FlatProgramAPI(currentProgram)
     val options = new DecompileOptions()
@@ -202,3 +185,14 @@ class Ghidra2Cpg(
 
   private class HeadlessGhidraProjectManager extends DefaultProjectManager {}
 }
+
+object Types {
+
+  // Types will be added to the CPG as soon as everything
+  // else is done
+  val types: mutable.SortedSet[String] = scala.collection.mutable.SortedSet[String]()
+  def registerType(typeName: String): String = {
+    types += typeName
+    typeName
+  }
+}
diff --git a/joern-cli/frontends/ghidra2cpg/src/main/scala/io/joern/ghidra2cpg/Main.scala b/joern-cli/frontends/ghidra2cpg/src/main/scala/io/joern/ghidra2cpg/Main.scala
index 9afa0021a..9bb6ddebf 100644
--- a/joern-cli/frontends/ghidra2cpg/src/main/scala/io/joern/ghidra2cpg/Main.scala
+++ b/joern-cli/frontends/ghidra2cpg/src/main/scala/io/joern/ghidra2cpg/Main.scala
@@ -1,6 +1,5 @@
 package io.joern.ghidra2cpg
 
-import io.joern.ghidra2cpg.Ghidra2Cpg
 import io.shiftleft.x2cpg.{X2Cpg, X2CpgConfig}
 import scopt.OParser
 
@@ -29,11 +28,7 @@ object Main extends App {
     case Some(config) =>
       if (config.inputPaths.size == 1) {
         val inputFile = new File(config.inputPaths.head)
-        val cpg = new Ghidra2Cpg(
-          inputFile,
-          Some(config.outputPath)
-        ).createCpg()
-        //cpg.close()
+        new Ghidra2Cpg().createCpg(inputFile, Some(config.outputPath))
       } else {
         println("This frontend requires exactly one input path")
         System.exit(1)
diff --git a/joern-cli/frontends/ghidra2cpg/src/main/scala/io/joern/ghidra2cpg/passes/mips/MipsFunctionPass.scala b/joern-cli/frontends/ghidra2cpg/src/main/scala/io/joern/ghidra2cpg/passes/mips/MipsFunctionPass.scala
index 253f1382c..3fca416fa 100644
--- a/joern-cli/frontends/ghidra2cpg/src/main/scala/io/joern/ghidra2cpg/passes/mips/MipsFunctionPass.scala
+++ b/joern-cli/frontends/ghidra2cpg/src/main/scala/io/joern/ghidra2cpg/passes/mips/MipsFunctionPass.scala
@@ -26,16 +26,15 @@ class MipsFunctionPass(currentProgram: Program,
                        keyPool: IntervalKeyPool,
                        decompInterface: DecompInterface)
     extends FunctionPass(new MipsProcessor, currentProgram, filename, function, cpg, keyPool, decompInterface) {
-  protected def findStringAtOffset(offset: Long) = {
-    DefinedDataIterator
-      .definedStrings(currentProgram)
-      .iterator()
-      .asScala
-      .filter(x => x.getAddress().getOffset == offset)
-      .toList
-      .map(x => x.getValue.toString)
-      .headOption
-  }
+
+  val address2Literals: Map[Long, String] = DefinedDataIterator
+    .definedStrings(currentProgram)
+    .iterator()
+    .asScala
+    .toList
+    .map(x => x.getAddress().getOffset -> x.getValue.toString)
+    .toMap
+
   val mipsCallInstructions = List("jalr", "jal")
   // Iterating over operands and add edges to call
   override def handleArguments(
@@ -78,12 +77,11 @@ class MipsFunctionPass(currentProgram: Program,
                   .typeFullName(input.getWordOffset.toHexString)
                 addArgumentEdge(callNode, node)
               } else if (input.isUnique) {
-                val value = findStringAtOffset(input.getDef.getInputs.toList.head.getAddress.getOffset + 8)
-                  .getOrElse(findStringAtOffset(input.getDef.getInputs.toList.head.getAddress.getOffset)
-                    .getOrElse(input.getDef.getInputs.toList.head.getAddress.getOffset))
+                val value = address2Literals.getOrElse(input.getDef.getInputs.toList.head.getAddress.getOffset,
+                                                       input.getDef.getInputs.toList.head.getAddress.getOffset.toString)
                 val node = nodes
                   .NewLiteral()
-                  .code(value.toString)
+                  .code(value)
                   .order(index)
                   .argumentIndex(index)
                   .typeFullName(input.getWordOffset.toHexString)


Test patch:
diff --git a/joern-cli/frontends/ghidra2cpg/src/test/scala/io/joern/ghidra2cpg/fixtures/GhidraBinToCpgSuite.scala b/joern-cli/frontends/ghidra2cpg/src/test/scala/io/joern/ghidra2cpg/fixtures/GhidraBinToCpgSuite.scala
index 7577c8fa0..7ab2a7ce4 100644
--- a/joern-cli/frontends/ghidra2cpg/src/test/scala/io/joern/ghidra2cpg/fixtures/GhidraBinToCpgSuite.scala
+++ b/joern-cli/frontends/ghidra2cpg/src/test/scala/io/joern/ghidra2cpg/fixtures/GhidraBinToCpgSuite.scala
@@ -26,10 +26,7 @@ class GhidraFrontend extends LanguageFrontend {
     Runtime.getRuntime.addShutdownHook(new Thread(() => FileUtils.deleteQuietly(tempDir)))
 
     val cpgBin = dir.getAbsolutePath
-    new Ghidra2Cpg(
-      inputFile,
-      Some(cpgBin)
-    ).createCpg()
+    new Ghidra2Cpg().createCpg(inputFile, Some(cpgBin))
 
     val odbConfig = overflowdb.Config.withDefaults().withStorageLocation(cpgBin)
     val config = CpgLoaderConfig.withDefaults.withOverflowConfig(odbConfig)