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
f2048c23
Unverified
Commit
f2048c23
authored
4 years ago
by
Enkelmann
Committed by
GitHub
4 years ago
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Add runtime memory image representation (#131)
parent
9a53c27b
master
…
v0.7
v0.6
v0.5
No related merge requests found
Show whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
263 additions
and
2 deletions
+263
-2
main.rs
caller/src/main.rs
+13
-1
lib.rs
cwe_checker_rs/src/lib.rs
+9
-1
binary.rs
cwe_checker_rs/src/utils/binary.rs
+240
-0
mod.rs
cwe_checker_rs/src/utils/mod.rs
+1
-0
No files found.
caller/src/main.rs
View file @
f2048c23
use
cwe_checker_rs
::
utils
::
binary
::
RuntimeMemoryImage
;
use
cwe_checker_rs
::
utils
::
log
::
print_all_messages
;
use
cwe_checker_rs
::
utils
::{
get_ghidra_plugin_path
,
read_config_file
};
use
cwe_checker_rs
::
AnalysisResults
;
...
...
@@ -135,7 +136,18 @@ fn run_with_ghidra(args: CmdlineArgs) {
let
mut
project
=
get_project_from_ghidra
(
&
binary_file_path
,
&
binary
[
..
],
args
.quiet
);
// Normalize the project and gather log messages generated from it.
let
mut
all_logs
=
project
.normalize
();
let
mut
analysis_results
=
AnalysisResults
::
new
(
&
binary
,
&
project
);
// Generate the representation of the runtime memory image of the binary
let
mut
runtime_memory_image
=
RuntimeMemoryImage
::
new
(
&
binary
)
.unwrap_or_else
(|
err
|
{
panic!
(
"Error while generating runtime memory image: {}"
,
err
);
});
if
project
.program.term.address_base_offset
!=
0
{
// We adjust the memory addresses once globally
// so that other analyses do not have to adjust their addresses.
runtime_memory_image
.add_global_memory_offset
(
project
.program.term.address_base_offset
);
}
let
mut
analysis_results
=
AnalysisResults
::
new
(
&
binary
,
&
runtime_memory_image
,
&
project
);
let
modules_depending_on_pointer_inference
=
vec!
[
"CWE243"
,
"CWE367"
,
"CWE476"
,
"Memory"
];
let
pointer_inference_results
=
if
modules
...
...
This diff is collapsed.
Click to expand it.
cwe_checker_rs/src/lib.rs
View file @
f2048c23
...
...
@@ -9,6 +9,7 @@ extern crate ocaml;
use
crate
::
analysis
::
pointer_inference
::
PointerInference
;
use
crate
::
intermediate_representation
::
Project
;
use
crate
::
utils
::
binary
::
RuntimeMemoryImage
;
use
crate
::
utils
::
log
::{
CweWarning
,
LogMessage
};
pub
mod
abstract_domain
;
...
...
@@ -75,6 +76,8 @@ pub fn get_modules() -> Vec<&'static CweModule> {
pub
struct
AnalysisResults
<
'a
>
{
/// The content of the binary file
pub
binary
:
&
'a
[
u8
],
/// A representation of the runtime memory image of the binary.
pub
runtime_memory_image
:
&
'a
RuntimeMemoryImage
,
/// A pointer to the project struct
pub
project
:
&
'a
Project
,
/// The result of the pointer inference analysis if already computed.
...
...
@@ -83,9 +86,14 @@ pub struct AnalysisResults<'a> {
impl
<
'a
>
AnalysisResults
<
'a
>
{
/// Create a new `AnalysisResults` struct with only the project itself known.
pub
fn
new
(
binary
:
&
'a
[
u8
],
project
:
&
'a
Project
)
->
AnalysisResults
<
'a
>
{
pub
fn
new
(
binary
:
&
'a
[
u8
],
runtime_memory_image
:
&
'a
RuntimeMemoryImage
,
project
:
&
'a
Project
,
)
->
AnalysisResults
<
'a
>
{
AnalysisResults
{
binary
,
runtime_memory_image
,
project
,
pointer_inference
:
None
,
}
...
...
This diff is collapsed.
Click to expand it.
cwe_checker_rs/src/utils/binary.rs
0 → 100644
View file @
f2048c23
//! Utility structs and functions which directly parse the binary file.
use
crate
::
abstract_domain
::
BitvectorDomain
;
use
crate
::
abstract_domain
::
HasByteSize
;
use
crate
::
abstract_domain
::
RegisterDomain
;
use
crate
::
intermediate_representation
::
BinOpType
;
use
crate
::
prelude
::
*
;
use
goblin
::
elf
;
use
goblin
::
pe
;
use
goblin
::
Object
;
/// A representation of the runtime image of a binary after being loaded into memory by the loader.
#[derive(Serialize,
Deserialize,
Debug,
PartialEq,
Eq,
Hash,
Clone)]
pub
struct
RuntimeMemoryImage
{
memory_segments
:
Vec
<
MemorySegment
>
,
is_little_endian
:
bool
,
}
/// A continuous segment in the memory image.
#[derive(Serialize,
Deserialize,
Debug,
PartialEq,
Eq,
Hash,
Clone)]
struct
MemorySegment
{
/// The contents of the segment
pub
bytes
:
Vec
<
u8
>
,
/// The base address, i.e. the address of the first byte of the segment
pub
base_address
:
u64
,
/// Is the segment readable
pub
read_flag
:
bool
,
/// Is the segment writeable
pub
write_flag
:
bool
,
/// Is the segment executable
pub
execute_flag
:
bool
,
}
impl
MemorySegment
{
/// Generate a segment from a program header of an ELF file.
pub
fn
from_elf_segment
(
binary
:
&
[
u8
],
program_header
:
&
elf
::
ProgramHeader
)
->
MemorySegment
{
let
mut
bytes
:
Vec
<
u8
>
=
binary
[
program_header
.file_range
()]
.to_vec
();
if
program_header
.vm_range
()
.len
()
>
program_header
.file_range
()
.len
()
{
// The additional memory space must be filled with null bytes.
bytes
.resize
(
program_header
.vm_range
()
.len
(),
0u8
);
}
MemorySegment
{
bytes
,
base_address
:
program_header
.p_vaddr
,
read_flag
:
program_header
.is_read
(),
write_flag
:
program_header
.is_write
(),
execute_flag
:
program_header
.is_executable
(),
}
}
/// Generate a segment from a section table from a PE file.
pub
fn
from_pe_section
(
binary
:
&
[
u8
],
section_header
:
&
pe
::
section_table
::
SectionTable
,
)
->
MemorySegment
{
let
mut
bytes
:
Vec
<
u8
>
=
binary
[
section_header
.pointer_to_raw_data
as
usize
..
(
section_header
.pointer_to_raw_data
as
usize
+
section_header
.size_of_raw_data
as
usize
)]
.to_vec
();
if
section_header
.virtual_size
>
section_header
.size_of_raw_data
{
// The additional memory space must be filled with null bytes.
bytes
.resize
(
section_header
.virtual_size
as
usize
,
0u8
);
}
MemorySegment
{
bytes
,
base_address
:
section_header
.virtual_address
as
u64
,
read_flag
:
(
section_header
.characteristics
&
0x40000000
)
!=
0
,
write_flag
:
(
section_header
.characteristics
&
0x80000000
)
!=
0
,
execute_flag
:
(
section_header
.characteristics
&
0x20000000
)
!=
0
,
}
}
}
impl
RuntimeMemoryImage
{
/// Generate a runtime memory image for a given binary.
///
/// The function can parse ELF and PE files as input.
pub
fn
new
(
binary
:
&
[
u8
])
->
Result
<
Self
,
Error
>
{
let
parsed_object
=
Object
::
parse
(
binary
)
?
;
match
parsed_object
{
Object
::
Elf
(
elf_file
)
=>
{
let
mut
memory_segments
=
Vec
::
new
();
for
header
in
elf_file
.program_headers
.iter
()
{
if
header
.p_type
==
elf
::
program_header
::
PT_LOAD
{
memory_segments
.push
(
MemorySegment
::
from_elf_segment
(
binary
,
header
));
}
}
if
memory_segments
.is_empty
()
{
return
Err
(
anyhow!
(
"No loadable segments found"
));
}
Ok
(
RuntimeMemoryImage
{
memory_segments
,
is_little_endian
:
elf_file
.header
.endianness
()
.unwrap
()
.is_little
(),
})
}
Object
::
PE
(
pe_file
)
=>
{
let
mut
memory_segments
=
Vec
::
new
();
for
header
in
pe_file
.sections
.iter
()
{
if
(
header
.characteristics
&
0x02000000
)
==
0
{
// Only load segments which are not discardable
memory_segments
.push
(
MemorySegment
::
from_pe_section
(
binary
,
header
));
}
}
if
memory_segments
.is_empty
()
{
return
Err
(
anyhow!
(
"No loadable segments found"
));
}
let
mut
memory_image
=
RuntimeMemoryImage
{
memory_segments
,
is_little_endian
:
true
,
};
memory_image
.add_global_memory_offset
(
pe_file
.image_base
as
u64
);
Ok
(
memory_image
)
}
_
=>
Err
(
anyhow!
(
"Object type not supported."
)),
}
}
/// Return whether values in the memory image should be interpreted in little-endian
/// or big-endian byte order.
pub
fn
is_little_endian_byte_order
(
&
self
)
->
bool
{
self
.is_little_endian
}
/// Add a global offset to the base addresses of all memory segments.
/// Useful to align the addresses with those reported by Ghidra
/// if the Ghidra backend added such an offset to all addresses.
pub
fn
add_global_memory_offset
(
&
mut
self
,
offset
:
u64
)
{
for
segment
in
self
.memory_segments
.iter_mut
()
{
segment
.base_address
+=
offset
;
}
}
/// Read the contents of the memory image at the given address into a `BitvectorDomain`,
/// to emulate a read instruction to global data at runtime.
///
/// The read method is endian-aware,
/// i.e. values are interpreted with the endianness of the CPU architecture.
/// If the address points to a writeable segment, the returned value is a `Top` value,
/// since the data may change during program execution.
///
/// Returns an error if the address is not contained in the global data address range.
pub
fn
read
(
&
self
,
address
:
&
Bitvector
,
size
:
ByteSize
)
->
Result
<
BitvectorDomain
,
Error
>
{
let
address
=
address
.try_to_u64
()
.unwrap
();
for
segment
in
self
.memory_segments
.iter
()
{
if
address
>=
segment
.base_address
&&
address
+
u64
::
from
(
size
)
<=
segment
.base_address
+
segment
.bytes
.len
()
as
u64
{
if
segment
.write_flag
{
// The segment is writeable, thus we do not know the content at runtime.
return
Ok
(
BitvectorDomain
::
new_top
(
size
));
}
let
index
=
(
address
-
segment
.base_address
)
as
usize
;
let
mut
bytes
=
segment
.bytes
[
index
..
index
+
u64
::
from
(
size
)
as
usize
]
.to_vec
();
if
self
.is_little_endian
{
bytes
=
bytes
.into_iter
()
.rev
()
.collect
();
}
let
mut
bytes
=
bytes
.into_iter
();
let
mut
bitvector
:
BitvectorDomain
=
Bitvector
::
from_u8
(
bytes
.next
()
.unwrap
())
.into
();
for
byte
in
bytes
{
let
new_byte
:
BitvectorDomain
=
Bitvector
::
from_u8
(
byte
)
.into
();
bitvector
=
bitvector
.bin_op
(
BinOpType
::
Piece
,
&
new_byte
);
}
return
Ok
(
bitvector
);
}
}
// No segment fully contains the read.
Err
(
anyhow!
(
"Address is not a valid global memory address."
))
}
/// For an address to global read-only memory, return the memory segment it points to
/// and the index inside the segment, where the address points to.
///
/// Returns an error if the target memory segment is marked as writeable
/// or if the pointer does not point to global memory.
pub
fn
get_ro_data_pointer_at_address
(
&
self
,
address
:
&
Bitvector
,
)
->
Result
<
(
&
[
u8
],
usize
),
Error
>
{
let
address
=
address
.try_to_u64
()
.unwrap
();
for
segment
in
self
.memory_segments
.iter
()
{
if
address
>=
segment
.base_address
&&
address
<
segment
.base_address
+
segment
.bytes
.len
()
as
u64
{
if
segment
.write_flag
{
return
Err
(
anyhow!
(
"Target segment is writeable"
));
}
else
{
return
Ok
((
&
segment
.bytes
,
(
address
-
segment
.base_address
)
as
usize
));
}
}
}
Err
(
anyhow!
(
"Pointer target not in global memory."
))
}
}
#[cfg(test)]
pub
mod
tests
{
use
super
::
*
;
impl
RuntimeMemoryImage
{
/// Create a mock runtime memory image for unit tests.
pub
fn
mock
()
->
RuntimeMemoryImage
{
RuntimeMemoryImage
{
memory_segments
:
vec!
[
MemorySegment
{
bytes
:
[
0xb0u8
,
0xb1
,
0xb2
,
0xb3
,
0xb4
]
.to_vec
(),
base_address
:
0x1000
,
read_flag
:
true
,
write_flag
:
false
,
execute_flag
:
false
,
}],
is_little_endian
:
true
,
}
}
}
#[test]
fn
read_endianness
()
{
let
mut
mem_image
=
RuntimeMemoryImage
::
mock
();
let
address
=
Bitvector
::
from_u32
(
0x1001
);
assert_eq!
(
mem_image
.read
(
&
address
,
ByteSize
::
new
(
4
))
.unwrap
(),
Bitvector
::
from_u32
(
0xb4b3b2b1
)
.into
()
);
mem_image
.is_little_endian
=
false
;
assert_eq!
(
mem_image
.read
(
&
address
,
ByteSize
::
new
(
4
))
.unwrap
(),
Bitvector
::
from_u32
(
0xb1b2b3b4
)
.into
()
);
}
#[test]
fn
ro_data_pointer
()
{
let
mem_image
=
RuntimeMemoryImage
::
mock
();
let
address
=
Bitvector
::
from_u32
(
0x1002
);
let
(
slice
,
index
)
=
mem_image
.get_ro_data_pointer_at_address
(
&
address
)
.unwrap
();
assert_eq!
(
index
,
2
);
assert_eq!
(
&
slice
[
index
..
],
&
[
0xb2u8
,
0xb3
,
0xb4
]);
}
}
This diff is collapsed.
Click to expand it.
cwe_checker_rs/src/utils/mod.rs
View file @
f2048c23
pub
mod
binary
;
pub
mod
graph_utils
;
pub
mod
log
;
pub
mod
symbol_utils
;
...
...
This diff is collapsed.
Click to expand it.
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