Repository: joernio/joern
Pull request #714 (2021-11-10 22:47:22)

Use RETURN nodes in ghidra2cpg
The CPG has so-called `RETURN` nodes to represent function returns, however, ghidra2cpg until now represented returns via `CALL` nodes. This PR fixes that and adapts the gadget finder script. In effect, the gadget finder now works across platforms and is no longer limited to MIPS.


Patch:
diff --git a/joern-cli/frontends/ghidra2cpg/src/main/scala/io/joern/ghidra2cpg/passes/FunctionPass.scala b/joern-cli/frontends/ghidra2cpg/src/main/scala/io/joern/ghidra2cpg/passes/FunctionPass.scala
index 28bb1947d..747a425b8 100644
--- a/joern-cli/frontends/ghidra2cpg/src/main/scala/io/joern/ghidra2cpg/passes/FunctionPass.scala
+++ b/joern-cli/frontends/ghidra2cpg/src/main/scala/io/joern/ghidra2cpg/passes/FunctionPass.scala
@@ -19,12 +19,14 @@ import io.joern.ghidra2cpg._
 import io.joern.ghidra2cpg.processors._
 import io.shiftleft.codepropertygraph.Cpg
 import io.shiftleft.codepropertygraph.generated.nodes.{
+  CfgNodeNew,
   NewBlock,
   NewCall,
   NewIdentifier,
   NewMethod,
   NewMethodParameterIn,
-  NewNode
+  NewNode,
+  NewReturn
 }
 import io.shiftleft.codepropertygraph.generated.{EdgeTypes, NodeTypes, nodes}
 import io.shiftleft.passes.{DiffGraph, IntervalKeyPool, ParallelCpgPass}
@@ -95,6 +97,15 @@ abstract class FunctionPass(
       .lineNumber(lineNumber)
   }
 
+  def createReturnNode(code: String, lineNumber: Integer): NewReturn = {
+    nodes
+      .NewReturn()
+      .code(code)
+      .order(0)
+      .argumentIndex(0)
+      .lineNumber(lineNumber)
+  }
+
   def createParameterNode(code: String, name: String, order: Int, typ: String): NewMethodParameterIn = {
     nodes
       .NewMethodParameterIn()
@@ -192,12 +203,12 @@ abstract class FunctionPass(
 
   def handleBody(): Unit = {
     if (instructions.nonEmpty) {
-      var prevInstructionNode = addCallNode(instructions.head)
+      var prevInstructionNode = addCallOrReturnNode(instructions.head)
       handleArguments(instructions.head, prevInstructionNode)
       diffGraph.addEdge(blockNode, prevInstructionNode, EdgeTypes.AST)
       diffGraph.addEdge(methodNode.get, prevInstructionNode, EdgeTypes.CFG)
       instructions.drop(1).foreach { instruction =>
-        val instructionNode = addCallNode(instruction)
+        val instructionNode = addCallOrReturnNode(instruction)
         diffGraph.addNode(instructionNode)
         handleArguments(instruction, instructionNode)
         diffGraph.addEdge(blockNode, instructionNode, EdgeTypes.AST)
@@ -210,7 +221,7 @@ abstract class FunctionPass(
   // Iterating over operands and add edges to call
   def handleArguments(
       instruction: Instruction,
-      callNode: NewCall
+      callNode: CfgNodeNew
   ): Unit = {
     val mnemonicString = instruction.getMnemonicString
     if (mnemonicString.equals("CALL")) {
@@ -331,12 +342,12 @@ abstract class FunctionPass(
     diffGraph.addEdge(fromNode, toNode, EdgeTypes.AST)
   }
 
-  def addCallNode(instruction: Instruction): NewCall = {
+  def addCallOrReturnNode(instruction: Instruction): CfgNodeNew = {
     processor.getInstructions
       .getOrElse(instruction.getMnemonicString, "UNKNOWN") match {
-      case "LEAVE" | "RET" =>
-        createCallNode("RET", "RET", instruction.getMinAddress.getOffsetAsBigInteger.intValue())
-      case "CALL" =>
+      case "RET" =>
+        createReturnNode(instruction.toString, instruction.getMinAddress.getOffsetAsBigInteger.intValue())
+      case "CALL" | "LEAVE" =>
         val code = sanitizeMethodName(
           codeUnitFormat.getOperandRepresentationString(instruction, 0)
         )
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 4817687c3..8f626820e 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
@@ -11,7 +11,7 @@ import io.joern.ghidra2cpg.passes.FunctionPass
 import io.joern.ghidra2cpg.processors.MipsProcessor
 import io.shiftleft.codepropertygraph.Cpg
 import io.shiftleft.codepropertygraph.generated.nodes
-import io.shiftleft.codepropertygraph.generated.nodes.NewCall
+import io.shiftleft.codepropertygraph.generated.nodes.{CfgNodeNew, NewCall}
 import io.shiftleft.passes.{DiffGraph, IntervalKeyPool}
 
 import scala.jdk.CollectionConverters._
@@ -29,7 +29,7 @@ class MipsFunctionPass(currentProgram: Program,
   // Iterating over operands and add edges to call
   override def handleArguments(
       instruction: Instruction,
-      callNode: NewCall
+      callNode: CfgNodeNew
   ): Unit = {
     val mnemonicString = instruction.getMnemonicString
     if (mipsCallInstructions.contains(mnemonicString)) {
diff --git a/joern-cli/frontends/ghidra2cpg/src/main/scala/io/joern/ghidra2cpg/passes/x86/X86FunctionPass.scala b/joern-cli/frontends/ghidra2cpg/src/main/scala/io/joern/ghidra2cpg/passes/x86/X86FunctionPass.scala
index 5931bfacc..0a9c39e6e 100644
--- a/joern-cli/frontends/ghidra2cpg/src/main/scala/io/joern/ghidra2cpg/passes/x86/X86FunctionPass.scala
+++ b/joern-cli/frontends/ghidra2cpg/src/main/scala/io/joern/ghidra2cpg/passes/x86/X86FunctionPass.scala
@@ -19,12 +19,12 @@ class X86FunctionPass(currentProgram: Program,
 
   override def handleBody(): Unit = {
     if (instructions.nonEmpty) {
-      var prevInstructionNode = addCallNode(instructions.head)
+      var prevInstructionNode = addCallOrReturnNode(instructions.head)
       handleArguments(instructions.head, prevInstructionNode)
       diffGraph.addEdge(blockNode, prevInstructionNode, EdgeTypes.AST)
       diffGraph.addEdge(methodNode.get, prevInstructionNode, EdgeTypes.CFG)
       instructions.drop(1).foreach { instruction =>
-        val instructionNode = addCallNode(instruction)
+        val instructionNode = addCallOrReturnNode(instruction)
         diffGraph.addNode(instructionNode)
         handleArguments(instruction, instructionNode)
         diffGraph.addEdge(blockNode, instructionNode, EdgeTypes.AST)
diff --git a/joern-cli/frontends/ghidra2cpg/src/main/scala/io/joern/ghidra2cpg/processors/ArmProcessor.scala b/joern-cli/frontends/ghidra2cpg/src/main/scala/io/joern/ghidra2cpg/processors/ArmProcessor.scala
index fc2ddb88b..0a04f4202 100644
--- a/joern-cli/frontends/ghidra2cpg/src/main/scala/io/joern/ghidra2cpg/processors/ArmProcessor.scala
+++ b/joern-cli/frontends/ghidra2cpg/src/main/scala/io/joern/ghidra2cpg/processors/ArmProcessor.scala
@@ -26,7 +26,7 @@ class ArmProcessor extends Processor {
       "mov" -> "<operator>.assignment",
       "movk" -> "<operator>.assignment",
       "nop" -> "<operator>.NOP",
-      "ret" -> "RETURN",
+      "ret" -> "RET",
       "stp" -> "<operator>.assignment",
       "str" -> "<operator>.addition",
       "strb" -> "<operator>.assignment",
diff --git a/joern-cli/frontends/ghidra2cpg/src/main/scala/io/joern/ghidra2cpg/processors/MipsProcessor.scala b/joern-cli/frontends/ghidra2cpg/src/main/scala/io/joern/ghidra2cpg/processors/MipsProcessor.scala
index f818a26dd..cf38c89d7 100644
--- a/joern-cli/frontends/ghidra2cpg/src/main/scala/io/joern/ghidra2cpg/processors/MipsProcessor.scala
+++ b/joern-cli/frontends/ghidra2cpg/src/main/scala/io/joern/ghidra2cpg/processors/MipsProcessor.scala
@@ -150,7 +150,7 @@ class MipsProcessor extends Processor {
       "jmp" -> "<operator>.goto",
       "jnc" -> "<operator>.goto",
       "jnz" -> "<operator>.goto",
-      "jr" -> "RETURN",
+      "jr" -> "RET",
       "jz" -> "<operator>.goto",
       "lb" -> "<operator>.assignment",
       "lbu" -> "<operator>.assignment",
diff --git a/joern-cli/frontends/ghidra2cpg/src/main/scala/io/joern/ghidra2cpg/processors/X86Processor.scala b/joern-cli/frontends/ghidra2cpg/src/main/scala/io/joern/ghidra2cpg/processors/X86Processor.scala
index 572f2825d..d54e7b8cd 100644
--- a/joern-cli/frontends/ghidra2cpg/src/main/scala/io/joern/ghidra2cpg/processors/X86Processor.scala
+++ b/joern-cli/frontends/ghidra2cpg/src/main/scala/io/joern/ghidra2cpg/processors/X86Processor.scala
@@ -56,7 +56,7 @@ class X86Processor extends Processor {
       "JS" -> "<operator>.goto",
       "JZ" -> "<operator>.goto",
       "LEA" -> "<operator>.addressOf",
-      "LEAVE" -> "RETURN",
+      "LEAVE" -> "LEAVE",
       "MOV" -> "<operator>.assignment",
       "MOVAPD" -> "<operator>.assignment",
       "MOVAPS" -> "<operator>.assignment",
@@ -83,7 +83,7 @@ class X86Processor extends Processor {
       "POP" -> "<operator>.assignment",
       "PUSH" -> "<operator>.assignment",
       "PXOR" -> "<operator>.assignmentXor",
-      "RET" -> "RETURN",
+      "RET" -> "RET",
       "ROL" -> "<operator>.rotateLeft",
       "ROR" -> "<operator>.rotateRight",
       "SAR" -> "<operator>.arithmeticShiftRight",
diff --git a/joern-cli/src/main/resources/scripts/binary/gadgets.sc b/joern-cli/src/main/resources/scripts/binary/gadgets.sc
new file mode 100644
index 000000000..b30b57d9b
--- /dev/null
+++ b/joern-cli/src/main/resources/scripts/binary/gadgets.sc
@@ -0,0 +1,5 @@
+def addrAndCode(y : CfgNode) =
+    y.address.get + ": " + y.code 
+def instrBefore(x : CfgNode, n: Int) = x.cfgPrev(n).l.reverse.map(addrAndCode)
+
+cpg.ret.map(x => instrBefore(x, 5).mkString("|\t") + "\n").l |> "/tmp/gadgets.txt"
diff --git a/joern-cli/src/main/resources/scripts/binary/mips-gadgets.sc b/joern-cli/src/main/resources/scripts/binary/mips-gadgets.sc
deleted file mode 100644
index 44321a6c1..000000000
--- a/joern-cli/src/main/resources/scripts/binary/mips-gadgets.sc
+++ /dev/null
@@ -1,6 +0,0 @@
-def addrAndCode(y : CfgNode) =
-    y.lineNumber.map(_.toLong.toHexString).get + ": " + y.code 
-def instrBefore(x : CfgNode, n: Int) = x.cfgPrev(n).l.reverse.map(addrAndCode)
-def rets = cpg.call.code(".*jr.*")
-
-rets.map(x => instrBefore(x, 5).mkString("|\t") + "\n").l |> "/tmp/gadgets.txt"


Test patch:
diff --git a/joern-cli/frontends/ghidra2cpg/src/test/scala/io/joern/ghidra2cpg/querying/mips/CallNodeTests.scala b/joern-cli/frontends/ghidra2cpg/src/test/scala/io/joern/ghidra2cpg/querying/mips/CallNodeTests.scala
index db72ac033..d92190954 100644
--- a/joern-cli/frontends/ghidra2cpg/src/test/scala/io/joern/ghidra2cpg/querying/mips/CallNodeTests.scala
+++ b/joern-cli/frontends/ghidra2cpg/src/test/scala/io/joern/ghidra2cpg/querying/mips/CallNodeTests.scala
@@ -11,8 +11,11 @@ class CallNodeTests extends GhidraBinToCpgSuite {
   }
 
   "CPG for binary of a simple program should not contain any CALL nodes with more than two arguments and the same ARGUMENT_INDEX value" in {
-    cpg.call.filter{ c =>
-      c.argument.size > 1 && c.argument.argumentIndex.toSet.size == 1
-    }.code.l shouldBe List()
+    cpg.call
+      .filter { c =>
+        c.argument.size > 1 && c.argument.argumentIndex.toSet.size == 1
+      }
+      .code
+      .l shouldBe List()
   }
 }