FazBrowse GitHub Viewer
|
Trending
|
URL:
|
Home
Tools:
[Download Repo ZIP]
[View Raw Code]
[Original HTTPS Page]
binaryninja-api/python/examples/rust_string.py at dev · Vector35/binaryninja-api · GitHub
Uh oh!
There was an error while loading.
Please reload this page
.
Vector35
/
binaryninja-api
Public
Notifications
You must be signed in to change notification settings
Fork
294
Star
1.3k
Code
Issues
1.9k
Pull requests
37
Discussions
Actions
Wiki
Security and quality
0
Insights
Additional navigation options
Code
Issues
Pull requests
Discussions
Actions
Wiki
Security and quality
Insights
Expand file tree
Breadcrumbs
binaryninja-api
/
python
/
examples
/
rust_string.py
Copy path
More file actions
More file actions
Latest commit
History
History
History
162 lines (128 loc) · 6.58 KB
Breadcrumbs
binaryninja-api
/
python
/
examples
/
rust_string.py
Copy path
File metadata and controls
162 lines (128 loc) · 6.58 KB
Raw
Copy raw file
Download raw file
Open symbols panel
Edit and raw actions
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
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
"""
Example Rust ``&str`` string recognizer and data renderer.
Rust represents a string slice (``&str``) as a two word "fat pointer": a pointer
to the UTF-8 bytes followed by the length of the slice. The type for it looks like::
struct &str
{
char* string; // offset 0: pointer to the UTF-8 bytes
uint64_t length; // offset 8: number of bytes
};
This plugin allows Binary Ninja to recover the underlying text in two situations:
* **Structure initializers.** When the optimizer folds the field assignments of
a ``&str`` value into a single ``HLIL_STRUCT_INIT`` expression, the recognizer
reads ``length`` bytes from ``string`` and renders the literal in place.
* **Constant pointers to ``&str`` data variables.** When code takes the address
of a ``&str`` data variable, the recognizer reads the fat pointer out of that
data variable and renders the string it points at.
The recognized strings use the ``rs`` prefix, so they render as ``rs"..."``. A
matching data renderer renders ``&str`` data variables the same way in linear view.
"""
from
typing
import
Dict
,
List
,
Optional
from
binaryninja
import
BinaryView
,
Type
from
binaryninja
.
datarender
import
DataRenderer
,
TypeContext
from
binaryninja
.
enums
import
(
DerivedStringLocationType
,
InstructionTextTokenType
,
TypeClass
)
from
binaryninja
.
function
import
DisassemblyTextLine
,
InstructionTextToken
from
binaryninja
.
highlevelil
import
HighLevelILFunction
,
HighLevelILInstruction
from
binaryninja
.
stringrecognizer
import
CustomStringType
,
StringRecognizer
from
binaryninja
.
types
import
NamedTypeReferenceType
from
binaryninja
.
binaryview
import
DerivedString
,
DerivedStringLocation
# Exact name of the Rust string slice type we recognize.
str_type_name
=
"&str"
# Register a custom string type so the core knows how to render the strings we
# recover. The prefix turns "..." into rs"...".
rust_str_type
=
CustomStringType
.
register
(
str_type_name
,
string_prefix
=
"rs"
)
def
_type_name
(
type
:
Optional
[
Type
])
->
Optional
[
str
]:
"""Return the registered/reference name of a type, or None if it has none.
A `&str` value arrives either as a named type reference (`type.name`) or,
once resolved, as the underlying structure carrying a registered name."""
if
type
is
None
:
return
None
if
isinstance
(
type
,
NamedTypeReferenceType
):
return
str
(
type
.
name
)
registered
=
type
.
registered_name
if
registered
is
not
None
:
return
str
(
registered
.
name
)
return
None
def
_is_str_type
(
type
:
Optional
[
Type
])
->
bool
:
"""True if `type` is exactly the `&str` type."""
return
_type_name
(
type
)
==
str_type_name
def
_is_pointer_to_str
(
type
:
Optional
[
Type
])
->
bool
:
"""True if `type` is a pointer to the `&str` type."""
return
type
is
not
None
and
type
.
type_class
==
TypeClass
.
PointerTypeClass
and
_is_str_type
(
type
.
target
)
def
_derived_string_from_slice
(
bv
:
BinaryView
,
pointer
:
int
,
length
:
int
)
->
Optional
[
DerivedString
]:
"""Read `length` UTF-8 bytes at `pointer` and wrap them in a DerivedString.
The returned string is data-backed location pointing at the bytes so
that the rendered literal cross-references the underlying string data."""
if
length
<
0
:
return
None
data
=
bv
.
read
(
pointer
,
length
)
if
data
is
None
or
len
(
data
)
!=
length
:
return
None
location
=
DerivedStringLocation
(
DerivedStringLocationType
.
DataBackedStringLocation
,
pointer
,
length
)
return
DerivedString
(
data
,
location
,
rust_str_type
)
def
_read_str_data_var
(
bv
:
BinaryView
,
addr
:
int
)
->
Optional
[
DerivedString
]:
"""Reads the `&str` fat pointer stored at `addr` and renders the string it points to."""
addr_size
=
bv
.
address_size
pointer
=
bv
.
read_pointer
(
addr
)
raw_length
=
bv
.
read
(
addr
+
addr_size
,
addr_size
)
if
raw_length
is
None
or
len
(
raw_length
)
!=
addr_size
:
return
None
length
=
int
.
from_bytes
(
raw_length
,
"little"
)
return
_derived_string_from_slice
(
bv
,
pointer
,
length
)
class
RustStrRecognizer
(
StringRecognizer
):
"""Recognizes Rust `&str` slices in HLIL expressions."""
recognizer_name
=
"Rust &str"
def
is_valid_for_type
(
self
,
func
:
HighLevelILFunction
,
type
:
Type
)
->
bool
:
# Run for `&str` structure initializers and for constant pointers to a
# `&str` data variable; skip every other expression type.
return
_is_str_type
(
type
)
or
_is_pointer_to_str
(
type
)
def
recognize_struct_init
(
self
,
instr
:
HighLevelILInstruction
,
type
:
Type
,
vals
:
Dict
[
int
,
int
]
)
->
Optional
[
DerivedString
]:
# `vals` maps each constant field offset to its value: offset 0 is the
# pointer to the bytes, offset at address size is the length of the slice.
addr_size
=
instr
.
function
.
view
.
address_size
if
0
not
in
vals
or
addr_size
not
in
vals
:
return
None
pointer
=
vals
[
0
]
length
=
vals
[
addr_size
]
return
_derived_string_from_slice
(
instr
.
function
.
view
,
pointer
,
length
)
def
recognize_constant_pointer
(
self
,
instr
:
HighLevelILInstruction
,
type
:
Type
,
val
:
int
)
->
Optional
[
DerivedString
]:
# Only resolve when a `&str` data variable actually lives at the pointer.
bv
=
instr
.
function
.
view
data_var
=
bv
.
get_data_var_at
(
val
)
if
data_var
is
None
or
not
_is_str_type
(
data_var
.
type
):
return
None
return
_read_str_data_var
(
bv
,
val
)
class
RustStrDataRenderer
(
DataRenderer
):
"""Renders `&str` data variables as `rs"..."` in linear view."""
def
perform_is_valid_for_data
(
self
,
ctxt
,
view
:
BinaryView
,
addr
:
int
,
type
:
Type
,
context
:
List
[
TypeContext
]
)
->
bool
:
return
_is_str_type
(
type
)
and
_read_str_data_var
(
view
,
addr
)
is
not
None
def
perform_get_lines_for_data
(
self
,
ctxt
,
view
:
BinaryView
,
addr
:
int
,
type
:
Type
,
prefix
:
List
[
InstructionTextToken
],
width
:
int
,
context
:
List
[
TypeContext
]
)
->
List
[
DisassemblyTextLine
]:
derived
=
_read_str_data_var
(
view
,
addr
)
tokens
=
list
(
prefix
)
if
derived
is
None
:
# We verified this in `perform_is_valid_for_data`, but handle the case of failing to
# fetch the string in case the data variable has changed since the check.
tokens
.
append
(
InstructionTextToken
(
InstructionTextTokenType
.
TextToken
,
str
(
type
)))
return
[
DisassemblyTextLine
(
tokens
,
addr
)]
# `&str` is UTF-8 by definition; escape control characters and quotes for display.
text
=
bytes
(
derived
.
value
).
decode
(
"utf-8"
,
"replace"
)
escaped
=
text
.
encode
(
"unicode_escape"
).
decode
(
"ascii"
).
replace
(
'"'
,
'
\\
"'
)
# `prefix` already carries the `<type> <name> = ` tokens, just append the literal.
tokens
.
append
(
InstructionTextToken
(
InstructionTextTokenType
.
BraceToken
,
f'rs"'
))
tokens
.
append
(
InstructionTextToken
(
InstructionTextTokenType
.
StringToken
,
escaped
))
tokens
.
append
(
InstructionTextToken
(
InstructionTextTokenType
.
BraceToken
,
'"'
))
return
[
DisassemblyTextLine
(
tokens
,
addr
)]
def
__del__
(
self
):
pass
RustStrRecognizer
().
register
()
RustStrDataRenderer
().
register_type_specific
()
Back
|
FazBrowse Home
|
New Git URL