8
8
Otherwise the exit status will be 1 and it will log which executables failed which checks.
9
9
'''
10
10
import sys
11
- from typing import List , Optional
11
+ from typing import List
12
12
13
13
import lief
14
14
15
- def check_ELF_RELRO (executable ) -> bool :
15
+ def check_ELF_RELRO (binary ) -> bool :
16
16
'''
17
17
Check for read-only relocations.
18
18
GNU_RELRO program header must exist
19
19
Dynamic section must have BIND_NOW flag
20
20
'''
21
- binary = lief .parse (executable )
22
21
have_gnu_relro = False
23
22
for segment in binary .segments :
24
23
# Note: not checking p_flags == PF_R: here as linkers set the permission differently
@@ -40,20 +39,18 @@ def check_ELF_RELRO(executable) -> bool:
40
39
41
40
return have_gnu_relro and have_bindnow
42
41
43
- def check_ELF_Canary (executable ) -> bool :
42
+ def check_ELF_Canary (binary ) -> bool :
44
43
'''
45
44
Check for use of stack canary
46
45
'''
47
- binary = lief .parse (executable )
48
46
return binary .has_symbol ('__stack_chk_fail' )
49
47
50
- def check_ELF_separate_code (executable ):
48
+ def check_ELF_separate_code (binary ):
51
49
'''
52
50
Check that sections are appropriately separated in virtual memory,
53
51
based on their permissions. This checks for missing -Wl,-z,separate-code
54
52
and potentially other problems.
55
53
'''
56
- binary = lief .parse (executable )
57
54
R = lief .ELF .SEGMENT_FLAGS .R
58
55
W = lief .ELF .SEGMENT_FLAGS .W
59
56
E = lief .ELF .SEGMENT_FLAGS .X
@@ -110,66 +107,56 @@ def check_ELF_separate_code(executable):
110
107
return False
111
108
return True
112
109
113
- def check_PE_DYNAMIC_BASE (executable ) -> bool :
110
+ def check_PE_DYNAMIC_BASE (binary ) -> bool :
114
111
'''PIE: DllCharacteristics bit 0x40 signifies dynamicbase (ASLR)'''
115
- binary = lief .parse (executable )
116
112
return lief .PE .DLL_CHARACTERISTICS .DYNAMIC_BASE in binary .optional_header .dll_characteristics_lists
117
113
118
114
# Must support high-entropy 64-bit address space layout randomization
119
115
# in addition to DYNAMIC_BASE to have secure ASLR.
120
- def check_PE_HIGH_ENTROPY_VA (executable ) -> bool :
116
+ def check_PE_HIGH_ENTROPY_VA (binary ) -> bool :
121
117
'''PIE: DllCharacteristics bit 0x20 signifies high-entropy ASLR'''
122
- binary = lief .parse (executable )
123
118
return lief .PE .DLL_CHARACTERISTICS .HIGH_ENTROPY_VA in binary .optional_header .dll_characteristics_lists
124
119
125
- def check_PE_RELOC_SECTION (executable ) -> bool :
120
+ def check_PE_RELOC_SECTION (binary ) -> bool :
126
121
'''Check for a reloc section. This is required for functional ASLR.'''
127
- binary = lief .parse (executable )
128
122
return binary .has_relocations
129
123
130
- def check_MACHO_NOUNDEFS (executable ) -> bool :
124
+ def check_MACHO_NOUNDEFS (binary ) -> bool :
131
125
'''
132
126
Check for no undefined references.
133
127
'''
134
- binary = lief .parse (executable )
135
128
return binary .header .has (lief .MachO .HEADER_FLAGS .NOUNDEFS )
136
129
137
- def check_MACHO_LAZY_BINDINGS (executable ) -> bool :
130
+ def check_MACHO_LAZY_BINDINGS (binary ) -> bool :
138
131
'''
139
132
Check for no lazy bindings.
140
133
We don't use or check for MH_BINDATLOAD. See #18295.
141
134
'''
142
- binary = lief .parse (executable )
143
135
return binary .dyld_info .lazy_bind == (0 ,0 )
144
136
145
- def check_MACHO_Canary (executable ) -> bool :
137
+ def check_MACHO_Canary (binary ) -> bool :
146
138
'''
147
139
Check for use of stack canary
148
140
'''
149
- binary = lief .parse (executable )
150
141
return binary .has_symbol ('___stack_chk_fail' )
151
142
152
- def check_PIE (executable ) -> bool :
143
+ def check_PIE (binary ) -> bool :
153
144
'''
154
145
Check for position independent executable (PIE),
155
146
allowing for address space randomization.
156
147
'''
157
- binary = lief .parse (executable )
158
148
return binary .is_pie
159
149
160
- def check_NX (executable ) -> bool :
150
+ def check_NX (binary ) -> bool :
161
151
'''
162
152
Check for no stack execution
163
153
'''
164
- binary = lief .parse (executable )
165
154
return binary .has_nx
166
155
167
- def check_control_flow (executable ) -> bool :
156
+ def check_control_flow (binary ) -> bool :
168
157
'''
169
158
Check for control flow instrumentation
170
159
'''
171
- binary = lief .parse (executable )
172
-
173
160
content = binary .get_content_from_virtual_address (binary .entrypoint , 4 , lief .Binary .VA_TYPES .AUTO )
174
161
175
162
if content == [243 , 15 , 30 , 250 ]: # endbr64
@@ -202,30 +189,20 @@ def check_control_flow(executable) -> bool:
202
189
]
203
190
}
204
191
205
- def identify_executable (executable ) -> Optional [str ]:
206
- with open (filename , 'rb' ) as f :
207
- magic = f .read (4 )
208
- if magic .startswith (b'MZ' ):
209
- return 'PE'
210
- elif magic .startswith (b'\x7f ELF' ):
211
- return 'ELF'
212
- elif magic .startswith (b'\xcf \xfa ' ):
213
- return 'MACHO'
214
- return None
215
-
216
192
if __name__ == '__main__' :
217
193
retval : int = 0
218
194
for filename in sys .argv [1 :]:
219
195
try :
220
- etype = identify_executable (filename )
221
- if etype is None :
222
- print (f'{ filename } : unknown format' )
196
+ binary = lief .parse (filename )
197
+ etype = binary .format .name
198
+ if etype == lief .EXE_FORMATS .UNKNOWN :
199
+ print (f'{ filename } : unknown executable format' )
223
200
retval = 1
224
201
continue
225
202
226
203
failed : List [str ] = []
227
204
for (name , func ) in CHECKS [etype ]:
228
- if not func (filename ):
205
+ if not func (binary ):
229
206
failed .append (name )
230
207
if failed :
231
208
print (f'{ filename } : failed { " " .join (failed )} ' )
0 commit comments