Skip to content

Commit 00b85d0

Browse files
committed
scripts: only parse the binary once in security-check.py
1 parent cad40a5 commit 00b85d0

File tree

1 file changed

+18
-41
lines changed

1 file changed

+18
-41
lines changed

contrib/devtools/security-check.py

Lines changed: 18 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -8,17 +8,16 @@
88
Otherwise the exit status will be 1 and it will log which executables failed which checks.
99
'''
1010
import sys
11-
from typing import List, Optional
11+
from typing import List
1212

1313
import lief
1414

15-
def check_ELF_RELRO(executable) -> bool:
15+
def check_ELF_RELRO(binary) -> bool:
1616
'''
1717
Check for read-only relocations.
1818
GNU_RELRO program header must exist
1919
Dynamic section must have BIND_NOW flag
2020
'''
21-
binary = lief.parse(executable)
2221
have_gnu_relro = False
2322
for segment in binary.segments:
2423
# Note: not checking p_flags == PF_R: here as linkers set the permission differently
@@ -40,20 +39,18 @@ def check_ELF_RELRO(executable) -> bool:
4039

4140
return have_gnu_relro and have_bindnow
4241

43-
def check_ELF_Canary(executable) -> bool:
42+
def check_ELF_Canary(binary) -> bool:
4443
'''
4544
Check for use of stack canary
4645
'''
47-
binary = lief.parse(executable)
4846
return binary.has_symbol('__stack_chk_fail')
4947

50-
def check_ELF_separate_code(executable):
48+
def check_ELF_separate_code(binary):
5149
'''
5250
Check that sections are appropriately separated in virtual memory,
5351
based on their permissions. This checks for missing -Wl,-z,separate-code
5452
and potentially other problems.
5553
'''
56-
binary = lief.parse(executable)
5754
R = lief.ELF.SEGMENT_FLAGS.R
5855
W = lief.ELF.SEGMENT_FLAGS.W
5956
E = lief.ELF.SEGMENT_FLAGS.X
@@ -110,66 +107,56 @@ def check_ELF_separate_code(executable):
110107
return False
111108
return True
112109

113-
def check_PE_DYNAMIC_BASE(executable) -> bool:
110+
def check_PE_DYNAMIC_BASE(binary) -> bool:
114111
'''PIE: DllCharacteristics bit 0x40 signifies dynamicbase (ASLR)'''
115-
binary = lief.parse(executable)
116112
return lief.PE.DLL_CHARACTERISTICS.DYNAMIC_BASE in binary.optional_header.dll_characteristics_lists
117113

118114
# Must support high-entropy 64-bit address space layout randomization
119115
# 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:
121117
'''PIE: DllCharacteristics bit 0x20 signifies high-entropy ASLR'''
122-
binary = lief.parse(executable)
123118
return lief.PE.DLL_CHARACTERISTICS.HIGH_ENTROPY_VA in binary.optional_header.dll_characteristics_lists
124119

125-
def check_PE_RELOC_SECTION(executable) -> bool:
120+
def check_PE_RELOC_SECTION(binary) -> bool:
126121
'''Check for a reloc section. This is required for functional ASLR.'''
127-
binary = lief.parse(executable)
128122
return binary.has_relocations
129123

130-
def check_MACHO_NOUNDEFS(executable) -> bool:
124+
def check_MACHO_NOUNDEFS(binary) -> bool:
131125
'''
132126
Check for no undefined references.
133127
'''
134-
binary = lief.parse(executable)
135128
return binary.header.has(lief.MachO.HEADER_FLAGS.NOUNDEFS)
136129

137-
def check_MACHO_LAZY_BINDINGS(executable) -> bool:
130+
def check_MACHO_LAZY_BINDINGS(binary) -> bool:
138131
'''
139132
Check for no lazy bindings.
140133
We don't use or check for MH_BINDATLOAD. See #18295.
141134
'''
142-
binary = lief.parse(executable)
143135
return binary.dyld_info.lazy_bind == (0,0)
144136

145-
def check_MACHO_Canary(executable) -> bool:
137+
def check_MACHO_Canary(binary) -> bool:
146138
'''
147139
Check for use of stack canary
148140
'''
149-
binary = lief.parse(executable)
150141
return binary.has_symbol('___stack_chk_fail')
151142

152-
def check_PIE(executable) -> bool:
143+
def check_PIE(binary) -> bool:
153144
'''
154145
Check for position independent executable (PIE),
155146
allowing for address space randomization.
156147
'''
157-
binary = lief.parse(executable)
158148
return binary.is_pie
159149

160-
def check_NX(executable) -> bool:
150+
def check_NX(binary) -> bool:
161151
'''
162152
Check for no stack execution
163153
'''
164-
binary = lief.parse(executable)
165154
return binary.has_nx
166155

167-
def check_control_flow(executable) -> bool:
156+
def check_control_flow(binary) -> bool:
168157
'''
169158
Check for control flow instrumentation
170159
'''
171-
binary = lief.parse(executable)
172-
173160
content = binary.get_content_from_virtual_address(binary.entrypoint, 4, lief.Binary.VA_TYPES.AUTO)
174161

175162
if content == [243, 15, 30, 250]: # endbr64
@@ -202,30 +189,20 @@ def check_control_flow(executable) -> bool:
202189
]
203190
}
204191

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'\x7fELF'):
211-
return 'ELF'
212-
elif magic.startswith(b'\xcf\xfa'):
213-
return 'MACHO'
214-
return None
215-
216192
if __name__ == '__main__':
217193
retval: int = 0
218194
for filename in sys.argv[1:]:
219195
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')
223200
retval = 1
224201
continue
225202

226203
failed: List[str] = []
227204
for (name, func) in CHECKS[etype]:
228-
if not func(filename):
205+
if not func(binary):
229206
failed.append(name)
230207
if failed:
231208
print(f'{filename}: failed {" ".join(failed)}')

0 commit comments

Comments
 (0)