1
- from dataclasses import dataclass
2
- from pprint import pformat
3
- from typing import List , Optional
1
+ try :
2
+ import importlib
4
3
5
- from binaryninja .binaryview import BinaryView , DataVariable
6
- from binaryninja .log import Logger
7
- from binaryninja .types import IntegerType , PointerType
4
+ importlib .import_module ("binaryninja" )
5
+ from .binja_plugin .plugin import plugin_init
8
6
9
- logger = Logger (session_id = 0 , logger_name = "rust_string_slicer" )
10
-
11
-
12
- @dataclass
13
- class StringSlice :
14
- address : int
15
- length : int
16
- data : bytes
17
-
18
- def __repr__ (self ):
19
- return f"StringSlice(address={ self .address :#x} , length={ self .length :#x} , data={ self .data } )"
20
-
21
-
22
- def recover_string_slices_from_readonly_data (
23
- bv : BinaryView ,
24
- ) -> Optional [List [StringSlice ]]:
25
- if bv .arch is None :
26
- logger .log_error ("Could not get architecture of current binary view, exiting" )
27
- return None
28
-
29
- readonly_segments = list (
30
- filter (
31
- lambda segment : segment .readable
32
- and not segment .writable
33
- and not segment .executable ,
34
- bv .segments ,
35
- )
36
- )
37
- if len (readonly_segments ) == 0 :
38
- logger .log_error ("Could not find any read-only segment in binary, exiting" )
39
- return None
40
-
41
- # Obtain all data vars which are pointers to data in readonly data segments
42
- data_vars_to_ro_segment_data : List [DataVariable ] = []
43
- for _data_var_addr , candidate_string_slice_data_ptr in bv .data_vars .items ():
44
- if isinstance (candidate_string_slice_data_ptr .type , PointerType ):
45
- for readonly_segment in readonly_segments :
46
- if candidate_string_slice_data_ptr .value in readonly_segment :
47
- data_vars_to_ro_segment_data .append (candidate_string_slice_data_ptr )
48
- logger .log_debug (
49
- f"Found pointer var at { candidate_string_slice_data_ptr .address :#x} ({ candidate_string_slice_data_ptr } ) pointing to { candidate_string_slice_data_ptr .value :#x} "
50
- )
51
-
52
- recovered_string_slices : List [StringSlice ] = []
53
- for candidate_string_slice_data_ptr in data_vars_to_ro_segment_data :
54
- # Try to read an integer following the data var,
55
- # and treat it as a candidate for a string slice length.
56
- candidate_string_slice_len_addr = (
57
- candidate_string_slice_data_ptr .address
58
- + candidate_string_slice_data_ptr .type .width
59
- )
60
-
61
- # Filter out anything at the candidate address
62
- # that's already defined as any data var type which is not an integer.
63
- existing_data_var_at_candidate_string_slice_len_addr = bv .get_data_var_at (
64
- candidate_string_slice_len_addr
65
- )
66
- if existing_data_var_at_candidate_string_slice_len_addr is not None :
67
- if not isinstance (
68
- existing_data_var_at_candidate_string_slice_len_addr .type , IntegerType
69
- ):
70
- continue
71
-
72
- candidate_string_slice_len = bv .read_int (
73
- address = candidate_string_slice_len_addr ,
74
- size = bv .arch .default_int_size ,
75
- sign = False ,
76
- endian = bv .arch .endianness ,
77
- )
78
-
79
- logger .log_debug (
80
- f"Pointer var at { candidate_string_slice_data_ptr .address :#x} is followed by integer with value { candidate_string_slice_len :#x} "
81
- )
82
-
83
- # Filter out any potential string slice which has length 0
84
- if candidate_string_slice_len == 0 :
85
- continue
86
-
87
- # Attempt to read out the pointed to value as a string slice, with the length obtained above.
88
- candidate_string_slice = bv .read (
89
- addr = candidate_string_slice_data_ptr .value ,
90
- length = candidate_string_slice_len ,
91
- )
92
-
93
- logger .log_debug (
94
- f"Obtained candidate string slice with addr { candidate_string_slice_data_ptr .value :#x} , len { candidate_string_slice_len :#x} : { candidate_string_slice } "
95
- )
96
-
97
- # Sanity check whether the recovered string is valid UTF-8
98
- try :
99
- candidate_utf8_string = candidate_string_slice .decode ("utf-8" )
100
- logger .log_info (
101
- f'Recovered string at addr { candidate_string_slice_data_ptr .value :#x} , len { candidate_string_slice_len :#x} : "{ candidate_utf8_string } "'
102
- )
103
-
104
- # Append the final string slice object to the list of recovered strings.
105
- recovered_string_slices .append (
106
- StringSlice (
107
- address = candidate_string_slice_data_ptr .value ,
108
- length = candidate_string_slice_len ,
109
- data = candidate_string_slice ,
110
- )
111
- )
112
- except UnicodeDecodeError as err :
113
- logger .log_warn (
114
- f"Candidate string slice { candidate_string_slice } does not decode to a valid UTF-8 string; excluding from final results: { err } "
115
- )
116
- continue
117
-
118
- return recovered_string_slices
119
-
120
-
121
- logger .log_info (pformat (recover_string_slices_from_readonly_data (bv )))
7
+ plugin_init ()
8
+ except ImportError :
9
+ pass
0 commit comments