Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
C
cwe_checker
Overview
Overview
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
fact-depend
cwe_checker
Commits
45ececd2
Commit
45ececd2
authored
Oct 21, 2020
by
Melvin Klimke
Committed by
Enkelmann
Nov 03, 2020
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Parse Calling Convention Register from Ghidra (#87)
parent
945cbf90
Hide whitespace changes
Inline
Side-by-side
Showing
5 changed files
with
515 additions
and
1 deletions
+515
-1
PcodeExtractor.java
ghidra/p_code_extractor/PcodeExtractor.java
+29
-0
ParseCspecContent.java
ghidra/p_code_extractor/internal/ParseCspecContent.java
+392
-0
PcodeBlockData.java
ghidra/p_code_extractor/internal/PcodeBlockData.java
+3
-0
RegisterConvention.java
ghidra/p_code_extractor/internal/RegisterConvention.java
+75
-0
Project.java
ghidra/p_code_extractor/term/Project.java
+16
-1
No files found.
ghidra/p_code_extractor/PcodeExtractor.java
View file @
45ececd2
import
java.io.FileNotFoundException
;
import
java.util.ArrayList
;
import
java.util.HashMap
;
import
java.util.List
;
import
java.util.Set
;
import
java.util.regex.Matcher
;
...
...
@@ -10,6 +12,7 @@ import org.apache.commons.lang3.EnumUtils;
import
bil.*
;
import
term.*
;
import
internal.*
;
import
symbol.ExternSymbol
;
import
serializer.Serializer
;
import
ghidra.app.script.GhidraScript
;
...
...
@@ -20,6 +23,7 @@ import ghidra.program.model.block.CodeBlockIterator;
import
ghidra.program.model.block.CodeBlockReferenceIterator
;
import
ghidra.program.model.block.SimpleBlockModel
;
import
ghidra.program.model.lang.CompilerSpec
;
import
ghidra.program.model.lang.PrototypeModel
;
import
ghidra.program.model.lang.Register
;
import
ghidra.program.model.listing.Function
;
import
ghidra.program.model.listing.FunctionIterator
;
...
...
@@ -28,6 +32,7 @@ import ghidra.program.model.listing.Instruction;
import
ghidra.program.model.listing.InstructionIterator
;
import
ghidra.program.model.listing.Listing
;
import
ghidra.program.model.listing.Parameter
;
import
ghidra.program.model.listing.VariableStorage
;
import
ghidra.program.model.pcode.PcodeOp
;
import
ghidra.program.model.pcode.Varnode
;
import
ghidra.program.model.symbol.Symbol
;
...
...
@@ -574,6 +579,14 @@ public class PcodeExtractor extends GhidraScript {
project
.
setProgram
(
program
);
project
.
setStackPointerRegister
(
stackPointerVar
);
project
.
setCpuArch
(
cpuArch
);
try
{
HashMap
<
String
,
RegisterConvention
>
conventions
=
new
HashMap
<
String
,
RegisterConvention
>();
ParseCspecContent
.
parseSpecs
(
ghidraProgram
,
conventions
);
addParameterRegister
(
conventions
);
project
.
setRegisterConvention
(
new
ArrayList
<
RegisterConvention
>(
conventions
.
values
()));
}
catch
(
FileNotFoundException
e
)
{
System
.
out
.
println
(
e
);
}
return
project
;
}
...
...
@@ -1066,4 +1079,20 @@ public class PcodeExtractor extends GhidraScript {
return
""
;
}
/**
* Adds parameter register to the RegisterCallingConvention object
*/
protected
void
addParameterRegister
(
HashMap
<
String
,
RegisterConvention
>
conventions
)
{
PrototypeModel
[]
models
=
ghidraProgram
.
getCompilerSpec
().
getCallingConventions
();
for
(
PrototypeModel
model
:
models
)
{
String
cconv
=
model
.
getName
();
if
(
conventions
.
get
(
cconv
)
!=
null
)
{
ArrayList
<
String
>
parameters
=
conventions
.
get
(
cconv
).
getParameter
();
for
(
VariableStorage
storage
:
model
.
getPotentialInputRegisterStorage
(
ghidraProgram
))
{
parameters
.
add
(
storage
.
getRegister
().
getName
());
}
}
}
}
}
ghidra/p_code_extractor/internal/ParseCspecContent.java
0 → 100644
View file @
45ececd2
package
internal
;
/**
* Most of the code below was inspired by Ghidra's source code at
* Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/lang/BasicCompilerSpec.java,
* Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/app/plugin/processors/sleigh/SleighLanguageProvider.java
*
* It extracts unaffected, killed by call and return registers for the given CPU architecture and adds them to the json output.
*
* For the identification of the correct .cspec file, the processor name is extracted from Ghidra to find the processor's
* corresponding .ldefs file. In the .ldefs file the language id and and compilerSpec id is used to determine the correct .cspec file.
*
* If the correct .cpsec file was found, it iterates over the XML DOM to extract the above mentioned registers.
*
*/
import
ghidra.xml.*
;
import
java.io.FileNotFoundException
;
import
java.io.IOException
;
import
java.util.ArrayList
;
import
java.util.HashMap
;
import
org.xml.sax.*
;
import
generic.jar.ResourceFile
;
import
ghidra.app.plugin.processors.sleigh.SleighException
;
import
ghidra.framework.Application
;
import
ghidra.program.model.lang.CompilerSpecID
;
import
ghidra.program.model.lang.CompilerSpecNotFoundException
;
import
ghidra.program.model.lang.LanguageID
;
import
ghidra.program.model.listing.Program
;
import
ghidra.util.Msg
;
public
class
ParseCspecContent
{
private
static
Exception
parseException
;
private
static
LanguageID
languageId
;
private
static
CompilerSpecID
compilerSpecId
;
private
static
Program
program
;
/**
*
* @param ghidraProgram
* @param conventions
* @throws FileNotFoundException
*
* Set the important parameters and handle the file extraction and parsing
*/
public
static
void
parseSpecs
(
Program
ghidraProgram
,
HashMap
<
String
,
RegisterConvention
>
conventions
)
throws
FileNotFoundException
{
program
=
ghidraProgram
;
languageId
=
program
.
getLanguageID
();
compilerSpecId
=
program
.
getCompilerSpec
().
getCompilerSpecID
();
ResourceFile
ldefFile
=
getLdefFile
();
if
(
ldefFile
!=
null
)
{
XmlPullParser
ldefParser
=
null
;
try
{
ldefParser
=
parseXmlFile
(
ldefFile
);
}
catch
(
CompilerSpecNotFoundException
e
)
{
System
.
out
.
println
(
e
);
}
String
cspecFileName
=
parseLdefFile
(
ldefParser
);
ResourceFile
cspecFile
=
getCspecFile
(
cspecFileName
);
if
(
cspecFile
!=
null
)
{
XmlPullParser
cspecParser
=
null
;
try
{
cspecParser
=
parseXmlFile
(
cspecFile
);
}
catch
(
CompilerSpecNotFoundException
e
)
{
System
.
out
.
println
(
e
);
}
parseCspecFile
(
cspecParser
,
conventions
);
}
else
{
throw
new
FileNotFoundException
(
"Could not find .cspec file."
);
}
}
else
{
throw
new
FileNotFoundException
(
"Could not find .ldef file."
);
}
}
/**
*
* @param file: File in Ghidra directory
* @return: XML parser for input file
* @throws CompilerSpecNotFoundException
*
* Handles all of the XML error handling and creates the Parser from the XMLPullParserFactory
*/
public
static
XmlPullParser
parseXmlFile
(
ResourceFile
file
)
throws
CompilerSpecNotFoundException
{
ErrorHandler
errHandler
=
new
ErrorHandler
()
{
@Override
public
void
error
(
SAXParseException
exception
)
throws
SAXException
{
parseException
=
exception
;
}
@Override
public
void
fatalError
(
SAXParseException
exception
)
throws
SAXException
{
parseException
=
exception
;
}
@Override
public
void
warning
(
SAXParseException
exception
)
throws
SAXException
{
Msg
.
warn
(
this
,
"Warning parsing '"
+
file
+
"'"
,
exception
);
}
};
try
{
XmlPullParser
parser
=
XmlPullParserFactory
.
create
(
file
,
errHandler
,
false
);
return
parser
;
}
catch
(
SleighException
e
)
{
parseException
=
e
;
Throwable
cause
=
e
.
getCause
();
if
(
cause
!=
null
)
{
if
(
cause
instanceof
SAXException
||
cause
instanceof
IOException
)
{
parseException
=
(
Exception
)
cause
;
}
}
}
catch
(
FileNotFoundException
e
)
{
parseException
=
e
;
}
catch
(
IOException
e
)
{
parseException
=
e
;
}
catch
(
SAXException
e
)
{
parseException
=
e
;
}
if
(
parseException
!=
null
)
{
throw
new
CompilerSpecNotFoundException
(
languageId
,
compilerSpecId
,
file
.
getName
(),
parseException
);
}
return
null
;
}
/**
*
* @return: resource file i.e. .ldef
*
* Searches the Ghidra's directories for .ldef extension and returns the corresponding
* file based on the processor name. In some cases the processor name has to be parsed
* for correct matching.
*/
public
static
ResourceFile
getLdefFile
()
{
String
processorDef
=
String
.
format
(
"%s.ldefs"
,
program
.
getLanguage
().
getLanguageDescription
().
getProcessor
().
toString
());
if
(
processorDef
.
startsWith
(
"MIPS"
))
{
processorDef
=
processorDef
.
toLowerCase
();
}
if
(
processorDef
.
startsWith
(
"PowerPC"
))
{
processorDef
=
"ppc.ldefs"
;
}
for
(
ResourceFile
file
:
Application
.
findFilesByExtensionInApplication
(
".ldefs"
))
{
if
(
file
.
getName
().
equals
(
processorDef
))
{
return
file
;
}
}
return
null
;
}
/**
*
* @param parser: parser for .ldef file
* @return: filename of .cspec file
*
* Parses the .cspec filename from the .ldef file by
* matching the language id. e.g. id = ARM:LE:32:v8
* to analyse the correct language.
*/
public
static
String
parseLdefFile
(
XmlPullParser
parser
)
{
String
cspecName
=
null
;
parser
.
start
(
"language_definitions"
);
while
(
parser
.
peek
().
isStart
())
{
XmlElement
languageEnter
=
parser
.
peek
();
if
(
languageEnter
.
getAttribute
(
"id"
).
equals
(
languageId
.
getIdAsString
()))
{
cspecName
=
getCompilerName
(
parser
);
break
;
}
else
{
discardSubTree
(
parser
);
}
}
parser
.
end
();
return
cspecName
;
}
/**
*
* @param parser
* @return
*
* Parses the compiler fields of the language and extracts the correct
* compiler using the compilerSpec Id e.g. id = default
*
* <compiler name="default" spec="ARM.cspec" id="default"/>
*/
public
static
String
getCompilerName
(
XmlPullParser
parser
)
{
String
cspec
=
null
;
parser
.
start
(
"language"
);
while
(
parser
.
peek
().
isStart
())
{
XmlElement
langProperty
=
parser
.
peek
();
if
(
langProperty
.
getName
().
equals
(
"compiler"
))
{
if
(
langProperty
.
getAttribute
(
"name"
).
equals
(
compilerSpecId
.
getIdAsString
()))
{
parser
.
start
();
cspec
=
langProperty
.
getAttribute
(
"spec"
);
parser
.
end
();
}
else
{
discardSubTree
(
parser
);
}
}
else
{
discardSubTree
(
parser
);
}
}
parser
.
end
();
return
cspec
;
}
/**
*
* @param parser: parser for .cspec file
* @param conventions
*
* Searches the .cspec file for default_proto or prototype wrapper
*/
public
static
void
parseCspecFile
(
XmlPullParser
parser
,
HashMap
<
String
,
RegisterConvention
>
conventions
)
{
parser
.
start
(
"compiler_spec"
);
while
(
parser
.
peek
().
isStart
())
{
String
field
=
parser
.
peek
().
getName
();
if
(
field
.
equals
(
"default_proto"
)
||
field
.
equals
(
"prototype"
))
{
parsePrototype
(
parser
,
field
,
conventions
);
}
else
{
discardSubTree
(
parser
);
}
}
parser
.
end
();
}
/**
*
* @param parser: parser for .cspec file
* @param name: name of the wrapper
* @param conventions
*
* Gets registers based on wrapper name. The default_proto wrapper
* is an additional wrapper around the default prototype. Therefore,
* the function has to go one level deeper.
*/
public
static
void
parsePrototype
(
XmlPullParser
parser
,
String
name
,
HashMap
<
String
,
RegisterConvention
>
conventions
)
{
RegisterConvention
convention
=
new
RegisterConvention
();
if
(
name
.
equals
(
"default_proto"
))
{
parser
.
start
();
getUnaffectedKilledByCallAndOutput
(
parser
,
convention
);
parser
.
end
();
}
else
if
(
name
.
equals
(
"prototype"
))
{
getUnaffectedKilledByCallAndOutput
(
parser
,
convention
);
}
// Using the hashmap this way will simplify the addition of parameter registers which are not parsed here
// as they are calling convention specific
conventions
.
put
(
convention
.
getCconv
(),
convention
);
}
/**
*
* @param parser: parser for .cspec file
* @param convention: convention object for later serialization
*
* Sets the convention's unaffected, killed by call and return registers as well as the calling convention
*/
public
static
void
getUnaffectedKilledByCallAndOutput
(
XmlPullParser
parser
,
RegisterConvention
convention
)
{
XmlElement
protoElement
=
parser
.
start
();
String
cconv
=
protoElement
.
getAttribute
(
"name"
);
convention
.
setCconv
(
cconv
);
while
(
parser
.
peek
().
isStart
())
{
XmlElement
registers
=
parser
.
peek
();
if
(
registers
.
getName
().
equals
(
"unaffected"
))
{
convention
.
setUnaffected
(
getRegisters
(
parser
));
}
else
if
(
registers
.
getName
().
equals
(
"killedbycall"
))
{
convention
.
setKilledByCall
(
getRegisters
(
parser
));
}
else
if
(
registers
.
getName
().
equals
(
"output"
))
{
convention
.
setReturn
(
parseOutput
(
parser
));
}
else
{
discardSubTree
(
parser
);
}
}
parser
.
end
(
protoElement
);
}
/**
*
* @param parser: parser for .cspec file
* @return: list of return registers
*
* Parses the output and pentry wrapper to access the return register fields
*/
public
static
ArrayList
<
String
>
parseOutput
(
XmlPullParser
parser
)
{
ArrayList
<
String
>
registers
=
new
ArrayList
<
String
>();
parser
.
start
(
"output"
);
while
(
parser
.
peek
().
isStart
())
{
parser
.
start
(
"pentry"
);
XmlElement
entry
=
parser
.
peek
();
if
(
entry
.
getName
().
equals
(
"register"
))
{
parser
.
start
(
"register"
);
registers
.
add
(
entry
.
getAttribute
(
"name"
));
parser
.
end
();
}
else
{
discardSubTree
(
parser
);
}
parser
.
end
();
}
parser
.
end
();
return
registers
;
}
/**
*
* @param parser: parser for .cspec file
* @return: either killed by call or unaffected registers
*
* Parses killed by call or unaffected registers and ingnores varnode types
*/
public
static
ArrayList
<
String
>
getRegisters
(
XmlPullParser
parser
)
{
ArrayList
<
String
>
registers
=
new
ArrayList
<
String
>();
parser
.
start
();
while
(
parser
.
peek
().
isStart
())
{
XmlElement
type
=
parser
.
peek
();
if
(
type
.
getName
().
equals
(
"register"
))
{
parser
.
start
();
registers
.
add
(
type
.
getAttribute
(
"name"
));
parser
.
end
();
}
else
if
(
type
.
getName
().
equals
(
"varnode"
))
{
discardSubTree
(
parser
);
}
}
parser
.
end
();
return
registers
;
}
/**
*
* @param filename: .cspec filename
* @return: .cspec file
*
* Return the .cspec file for a given filename
*/
public
static
ResourceFile
getCspecFile
(
String
filename
)
{
for
(
ResourceFile
file
:
Application
.
findFilesByExtensionInApplication
(
".cspec"
))
{
if
(
file
.
getName
().
equals
(
filename
))
{
return
file
;
}
}
return
null
;
}
/**
*
* @param parser: xml parser
*
* discards XML subtrees if they do not contain necessary information.
* Simply used as a shortcut.
*/
public
static
void
discardSubTree
(
XmlPullParser
parser
)
{
XmlElement
el
=
parser
.
start
();
parser
.
discardSubTree
(
el
);
}
}
ghidra/p_code_extractor/PcodeBlockData.java
→
ghidra/p_code_extractor/
internal/
PcodeBlockData.java
View file @
45ececd2
package
internal
;
import
java.util.ArrayList
;
import
ghidra.program.model.listing.Instruction
;
...
...
ghidra/p_code_extractor/internal/RegisterConvention.java
0 → 100644
View file @
45ececd2
package
internal
;
import
java.util.ArrayList
;
import
com.google.gson.annotations.SerializedName
;
public
class
RegisterConvention
{
@SerializedName
(
"calling_convention"
)
private
String
cconv
;
@SerializedName
(
"parameter_register"
)
private
ArrayList
<
String
>
parameter
;
@SerializedName
(
"return_register"
)
private
ArrayList
<
String
>
return_
;
@SerializedName
(
"unaffected_register"
)
private
ArrayList
<
String
>
unaffected
;
@SerializedName
(
"killed_by_call_register"
)
private
ArrayList
<
String
>
killedByCall
;
public
RegisterConvention
()
{
this
.
setParameter
(
new
ArrayList
<
String
>());
this
.
setReturn
(
new
ArrayList
<
String
>());
this
.
setUnaffected
(
new
ArrayList
<
String
>());
this
.
setKilledByCall
(
new
ArrayList
<
String
>());
}
public
RegisterConvention
(
String
cconv
,
ArrayList
<
String
>
parameter
,
ArrayList
<
String
>
return_
,
ArrayList
<
String
>
unaffected
,
ArrayList
<
String
>
killedByCall
)
{
this
.
setCconv
(
cconv
);
this
.
setParameter
(
parameter
);
this
.
setReturn
(
return_
);
this
.
setUnaffected
(
unaffected
);
this
.
setKilledByCall
(
killedByCall
);
}
public
String
getCconv
()
{
return
cconv
;
}
public
void
setCconv
(
String
cconv
)
{
this
.
cconv
=
cconv
;
}
public
ArrayList
<
String
>
getParameter
()
{
return
parameter
;
}
public
void
setParameter
(
ArrayList
<
String
>
parameter
)
{
this
.
parameter
=
parameter
;
}
public
ArrayList
<
String
>
getReturn
()
{
return
return_
;
}
public
void
setReturn
(
ArrayList
<
String
>
return_
)
{
this
.
return_
=
return_
;
}
public
ArrayList
<
String
>
getUnaffected
()
{
return
unaffected
;
}
public
void
setUnaffected
(
ArrayList
<
String
>
unaffected
)
{
this
.
unaffected
=
unaffected
;
}
public
ArrayList
<
String
>
getKilledByCall
()
{
return
killedByCall
;
}
public
void
setKilledByCall
(
ArrayList
<
String
>
killedByCall
)
{
this
.
killedByCall
=
killedByCall
;
}
}
ghidra/p_code_extractor/term/Project.java
View file @
45ececd2
package
term
;
import
bil.Variable
;
import
internal.RegisterConvention
;
import
java.util.ArrayList
;
import
com.google.gson.annotations.SerializedName
;
public
class
Project
{
...
...
@@ -10,14 +14,17 @@ public class Project {
private
Variable
stackPointerRegister
;
@SerializedName
(
"cpu_architecture"
)
private
String
cpuArch
;
@SerializedName
(
"register_calling_convention"
)
private
ArrayList
<
RegisterConvention
>
conventions
;
public
Project
()
{
}
public
Project
(
Term
<
Program
>
program
,
String
cpuArch
,
Variable
stackPointerRegister
)
{
public
Project
(
Term
<
Program
>
program
,
String
cpuArch
,
Variable
stackPointerRegister
,
ArrayList
<
RegisterConvention
>
conventions
)
{
this
.
setProgram
(
program
);
this
.
setCpuArch
(
cpuArch
);
this
.
setStackPointerRegister
(
stackPointerRegister
);
this
.
setRegisterConvention
(
conventions
);
}
public
Term
<
Program
>
getProgram
()
{
...
...
@@ -43,4 +50,12 @@ public class Project {
public
void
setCpuArch
(
String
cpuArch
)
{
this
.
cpuArch
=
cpuArch
;
}
public
ArrayList
<
RegisterConvention
>
getRegisterConvention
()
{
return
conventions
;
}
public
void
setRegisterConvention
(
ArrayList
<
RegisterConvention
>
conventions
)
{
this
.
conventions
=
conventions
;
}
}
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment