2018-08-13 18:07:52 +02:00
|
|
|
#!/usr/bin/env python3
|
2014-04-24 17:43:22 +02:00
|
|
|
# Copyright (c) 2014 Wladimir J. van der Laan
|
2014-12-13 05:09:33 +01:00
|
|
|
# Distributed under the MIT software license, see the accompanying
|
2014-04-24 17:43:22 +02:00
|
|
|
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
|
|
|
'''
|
2021-07-07 13:21:30 +02:00
|
|
|
A script to check that the (Linux) release executables only contain
|
2020-03-25 15:32:39 +01:00
|
|
|
certain symbols and are only linked against allowed libraries.
|
2014-04-24 17:43:22 +02:00
|
|
|
|
|
|
|
Example usage:
|
|
|
|
|
2021-07-07 13:21:30 +02:00
|
|
|
find ../path/to/binaries -type f -executable | xargs python3 contrib/devtools/symbol-check.py
|
2014-04-24 17:43:22 +02:00
|
|
|
'''
|
|
|
|
import sys
|
2020-12-28 14:18:02 +01:00
|
|
|
from typing import Dict, List
|
2020-11-20 09:15:44 +01:00
|
|
|
|
2023-05-13 19:44:39 +02:00
|
|
|
import lief
|
2014-04-24 17:43:22 +02:00
|
|
|
|
2023-07-28 15:36:36 +02:00
|
|
|
# Debian 11 (Bullseye) EOL: est. 2026 https://wiki.debian.org/LTS
|
2014-04-24 17:43:22 +02:00
|
|
|
#
|
2023-07-28 15:36:36 +02:00
|
|
|
# - libgcc version 10.2.1 (https://packages.debian.org/search?suite=bullseye&arch=any&searchon=names&keywords=libgcc-s1)
|
|
|
|
# - libc version 2.31 (https://packages.debian.org/search?suite=bullseye&arch=any&searchon=names&keywords=libc6)
|
2014-04-24 17:43:22 +02:00
|
|
|
#
|
2023-05-31 09:25:12 +02:00
|
|
|
# Ubuntu 20.04 (Focal) EOL: 2030. https://wiki.ubuntu.com/ReleaseTeam
|
2014-04-24 17:43:22 +02:00
|
|
|
#
|
2023-05-31 09:25:12 +02:00
|
|
|
# - libgcc version 10.3.0 (https://packages.ubuntu.com/focal/libgcc1)
|
|
|
|
# - libc version 2.31 (https://packages.ubuntu.com/focal/libc6)
|
2019-11-20 10:46:01 +01:00
|
|
|
#
|
2023-07-28 15:36:36 +02:00
|
|
|
# CentOS Stream 9 EOL: est. 2027 https://www.centos.org/cl-vs-cs
|
2019-11-20 10:46:01 +01:00
|
|
|
#
|
2023-07-28 15:36:36 +02:00
|
|
|
# - libgcc version 12.2.1 (https://mirror.stream.centos.org/9-stream/AppStream/x86_64/os/Packages)
|
|
|
|
# - libc version 2.34 (https://mirror.stream.centos.org/9-stream/AppStream/x86_64/os/Packages)
|
2014-04-24 17:43:22 +02:00
|
|
|
#
|
2021-11-10 09:23:07 +01:00
|
|
|
# See https://gcc.gnu.org/onlinedocs/libstdc++/manual/abi.html for more info.
|
|
|
|
|
2014-04-24 17:43:22 +02:00
|
|
|
MAX_VERSIONS = {
|
2023-12-18 13:44:42 +01:00
|
|
|
'GCC': (4,3,0),
|
2021-06-14 20:24:52 +02:00
|
|
|
'GLIBC': {
|
2023-07-28 15:36:36 +02:00
|
|
|
lief.ELF.ARCH.x86_64: (2,31),
|
|
|
|
lief.ELF.ARCH.ARM: (2,31),
|
|
|
|
lief.ELF.ARCH.AARCH64:(2,31),
|
|
|
|
lief.ELF.ARCH.PPC64: (2,31),
|
|
|
|
lief.ELF.ARCH.RISCV: (2,31),
|
2021-06-14 20:24:52 +02:00
|
|
|
},
|
2021-06-14 20:28:26 +02:00
|
|
|
'LIBATOMIC': (1,0),
|
|
|
|
'V': (0,5,0), # xkb (bitcoin-qt only)
|
2014-04-24 17:43:22 +02:00
|
|
|
}
|
2016-01-26 20:50:50 +01:00
|
|
|
|
2014-08-17 10:06:20 +02:00
|
|
|
# Ignore symbols that are exported as part of every executable
|
|
|
|
IGNORE_EXPORTS = {
|
2023-05-13 17:23:21 +02:00
|
|
|
'_edata', '_end', '__end__', '_init', '__bss_start', '__bss_start__', '_bss_end__',
|
|
|
|
'__bss_end__', '_fini', '_IO_stdin_used', 'stdin', 'stdout', 'stderr',
|
2019-11-25 01:07:21 +01:00
|
|
|
'environ', '_environ', '__environ',
|
2023-05-19 18:19:31 +02:00
|
|
|
# Used in stacktraces.cpp
|
|
|
|
'__cxa_demangle'
|
2014-08-17 10:06:20 +02:00
|
|
|
}
|
2020-01-22 20:32:35 +01:00
|
|
|
|
2021-09-28 02:26:08 +02:00
|
|
|
# Expected linker-loader names can be found here:
|
|
|
|
# https://sourceware.org/glibc/wiki/ABIList?action=recall&rev=16
|
|
|
|
ELF_INTERPRETER_NAMES: Dict[lief.ELF.ARCH, Dict[lief.ENDIANNESS, str]] = {
|
|
|
|
lief.ELF.ARCH.x86_64: {
|
|
|
|
lief.ENDIANNESS.LITTLE: "/lib64/ld-linux-x86-64.so.2",
|
|
|
|
},
|
|
|
|
lief.ELF.ARCH.ARM: {
|
|
|
|
lief.ENDIANNESS.LITTLE: "/lib/ld-linux-armhf.so.3",
|
|
|
|
},
|
|
|
|
lief.ELF.ARCH.AARCH64: {
|
|
|
|
lief.ENDIANNESS.LITTLE: "/lib/ld-linux-aarch64.so.1",
|
|
|
|
},
|
|
|
|
lief.ELF.ARCH.PPC64: {
|
|
|
|
lief.ENDIANNESS.BIG: "/lib64/ld64.so.1",
|
|
|
|
lief.ENDIANNESS.LITTLE: "/lib64/ld64.so.2",
|
|
|
|
},
|
2023-05-13 17:27:38 +02:00
|
|
|
lief.ELF.ARCH.RISCV: {
|
2021-09-28 02:26:08 +02:00
|
|
|
lief.ENDIANNESS.LITTLE: "/lib/ld-linux-riscv64-lp64d.so.1",
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
2023-05-03 10:48:44 +02:00
|
|
|
ELF_ABIS: Dict[lief.ELF.ARCH, Dict[lief.ENDIANNESS, List[int]]] = {
|
|
|
|
lief.ELF.ARCH.x86_64: {
|
|
|
|
lief.ENDIANNESS.LITTLE: [3,2,0],
|
|
|
|
},
|
|
|
|
lief.ELF.ARCH.ARM: {
|
|
|
|
lief.ENDIANNESS.LITTLE: [3,2,0],
|
|
|
|
},
|
|
|
|
lief.ELF.ARCH.AARCH64: {
|
|
|
|
lief.ENDIANNESS.LITTLE: [3,7,0],
|
|
|
|
},
|
|
|
|
lief.ELF.ARCH.PPC64: {
|
|
|
|
lief.ENDIANNESS.LITTLE: [3,10,0],
|
|
|
|
lief.ENDIANNESS.BIG: [3,2,0],
|
|
|
|
},
|
|
|
|
lief.ELF.ARCH.RISCV: {
|
|
|
|
lief.ENDIANNESS.LITTLE: [4,15,0],
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
2015-10-19 14:53:56 +02:00
|
|
|
# Allowed NEEDED libraries
|
2020-01-22 20:32:35 +01:00
|
|
|
ELF_ALLOWED_LIBRARIES = {
|
2020-06-11 10:39:04 +02:00
|
|
|
# dashd and dash-qt
|
2018-08-13 18:07:52 +02:00
|
|
|
'libgcc_s.so.1', # GCC base support
|
|
|
|
'libc.so.6', # C library
|
|
|
|
'libpthread.so.0', # threading
|
|
|
|
'libm.so.6', # math library
|
|
|
|
'librt.so.1', # real-time (clock)
|
2018-08-31 12:57:47 +02:00
|
|
|
'libatomic.so.1',
|
2018-08-13 18:07:52 +02:00
|
|
|
'ld-linux-x86-64.so.2', # 64-bit dynamic linker
|
|
|
|
'ld-linux.so.2', # 32-bit dynamic linker
|
2018-08-31 12:57:47 +02:00
|
|
|
'ld-linux-aarch64.so.1', # 64-bit ARM dynamic linker
|
|
|
|
'ld-linux-armhf.so.3', # 32-bit ARM dynamic linker
|
2020-11-20 09:15:44 +01:00
|
|
|
'ld64.so.1', # POWER64 ABIv1 dynamic linker
|
|
|
|
'ld64.so.2', # POWER64 ABIv2 dynamic linker
|
2018-08-31 12:57:47 +02:00
|
|
|
'ld-linux-riscv64-lp64d.so.1', # 64-bit RISC-V dynamic linker
|
2023-05-19 18:19:31 +02:00
|
|
|
'libz.so.1', # zlib
|
2020-06-11 10:39:04 +02:00
|
|
|
# dash-qt only
|
2018-08-13 18:07:52 +02:00
|
|
|
'libxcb.so.1', # part of X11
|
2023-05-19 18:19:31 +02:00
|
|
|
'libxcb-shm.so.0', # X11 shared memory extension
|
2021-11-25 05:02:50 +01:00
|
|
|
'libxkbcommon.so.0', # keyboard keymapping
|
|
|
|
'libxkbcommon-x11.so.0', # keyboard keymapping
|
2018-08-13 18:07:52 +02:00
|
|
|
'libfontconfig.so.1', # font support
|
|
|
|
'libfreetype.so.6', # font parsing
|
2023-08-03 13:45:17 +02:00
|
|
|
'libdl.so.2', # programming interface to dynamic linker
|
|
|
|
'libxcb-icccm.so.4',
|
|
|
|
'libxcb-image.so.0',
|
|
|
|
'libxcb-shm.so.0',
|
|
|
|
'libxcb-keysyms.so.1',
|
|
|
|
'libxcb-randr.so.0',
|
|
|
|
'libxcb-render-util.so.0',
|
|
|
|
'libxcb-render.so.0',
|
|
|
|
'libxcb-shape.so.0',
|
|
|
|
'libxcb-sync.so.1',
|
|
|
|
'libxcb-xfixes.so.0',
|
|
|
|
'libxcb-xinerama.so.0',
|
|
|
|
'libxcb-xkb.so.1',
|
2015-10-28 18:08:53 +01:00
|
|
|
}
|
2020-01-22 20:32:35 +01:00
|
|
|
|
|
|
|
MACHO_ALLOWED_LIBRARIES = {
|
|
|
|
# bitcoind and bitcoin-qt
|
|
|
|
'libc++.1.dylib', # C++ Standard Library
|
|
|
|
'libSystem.B.dylib', # libc, libm, libpthread, libinfo
|
|
|
|
# bitcoin-qt only
|
|
|
|
'AppKit', # user interface
|
|
|
|
'ApplicationServices', # common application tasks.
|
|
|
|
'Carbon', # deprecated c back-compat API
|
2023-08-03 13:45:17 +02:00
|
|
|
'ColorSync',
|
2020-01-22 20:32:35 +01:00
|
|
|
'CoreFoundation', # low level func, data types
|
|
|
|
'CoreGraphics', # 2D rendering
|
|
|
|
'CoreServices', # operating system services
|
|
|
|
'CoreText', # interface for laying out text and handling fonts.
|
2022-08-23 21:50:43 +02:00
|
|
|
'CoreVideo', # video processing
|
2020-01-22 20:32:35 +01:00
|
|
|
'Foundation', # base layer functionality for apps/frameworks
|
|
|
|
'ImageIO', # read and write image file formats.
|
|
|
|
'IOKit', # user-space access to hardware devices and drivers.
|
2022-08-23 21:50:43 +02:00
|
|
|
'IOSurface', # cross process image/drawing buffers
|
2020-01-22 20:32:35 +01:00
|
|
|
'libobjc.A.dylib', # Objective-C runtime library
|
2022-08-23 21:50:43 +02:00
|
|
|
'Metal', # 3D graphics
|
|
|
|
'Security', # access control and authentication
|
|
|
|
'QuartzCore', # animation
|
2020-01-22 20:32:35 +01:00
|
|
|
}
|
|
|
|
|
2020-03-25 15:32:39 +01:00
|
|
|
PE_ALLOWED_LIBRARIES = {
|
|
|
|
'ADVAPI32.dll', # security & registry
|
|
|
|
'IPHLPAPI.DLL', # IP helper API
|
|
|
|
'KERNEL32.dll', # win32 base APIs
|
|
|
|
'msvcrt.dll', # C standard library for MSVC
|
|
|
|
'SHELL32.dll', # shell API
|
|
|
|
'WS2_32.dll', # sockets
|
|
|
|
'bcrypt.dll',
|
|
|
|
# bitcoin-qt only
|
|
|
|
'dwmapi.dll', # desktop window manager
|
|
|
|
'GDI32.dll', # graphics device interface
|
|
|
|
'IMM32.dll', # input method editor
|
2023-07-20 14:16:18 +02:00
|
|
|
'NETAPI32.dll', # network management
|
2020-03-25 15:32:39 +01:00
|
|
|
'ole32.dll', # component object model
|
|
|
|
'OLEAUT32.dll', # OLE Automation API
|
|
|
|
'SHLWAPI.dll', # light weight shell API
|
2023-07-20 14:16:18 +02:00
|
|
|
'USER32.dll', # user interface
|
|
|
|
'USERENV.dll', # user management
|
|
|
|
'UxTheme.dll', # visual style
|
2020-03-25 15:32:39 +01:00
|
|
|
'VERSION.dll', # version checking
|
|
|
|
'WINMM.dll', # WinMM audio API
|
2023-07-20 14:16:18 +02:00
|
|
|
'WTSAPI32.dll', # Remote Desktop
|
2020-03-25 15:32:39 +01:00
|
|
|
}
|
|
|
|
|
2020-01-22 20:32:35 +01:00
|
|
|
def check_version(max_versions, version, arch) -> bool:
|
2023-05-13 17:23:21 +02:00
|
|
|
(lib, _, ver) = version.rpartition('_')
|
2018-08-13 18:07:52 +02:00
|
|
|
ver = tuple([int(x) for x in ver.split('.')])
|
2014-04-24 17:43:22 +02:00
|
|
|
if not lib in max_versions:
|
|
|
|
return False
|
2021-06-14 20:24:52 +02:00
|
|
|
if isinstance(max_versions[lib], tuple):
|
|
|
|
return ver <= max_versions[lib]
|
|
|
|
else:
|
|
|
|
return ver <= max_versions[lib][arch]
|
2014-04-24 17:43:22 +02:00
|
|
|
|
2023-05-13 17:23:21 +02:00
|
|
|
def check_imported_symbols(binary) -> bool:
|
2020-12-28 14:18:02 +01:00
|
|
|
ok: bool = True
|
2020-11-20 09:15:44 +01:00
|
|
|
|
2023-05-13 17:23:21 +02:00
|
|
|
for symbol in binary.imported_symbols:
|
|
|
|
if not symbol.imported:
|
2020-11-20 09:15:44 +01:00
|
|
|
continue
|
2023-05-13 17:23:21 +02:00
|
|
|
|
|
|
|
version = symbol.symbol_version if symbol.has_version else None
|
|
|
|
|
|
|
|
if version:
|
|
|
|
aux_version = version.symbol_version_auxiliary.name if version.has_auxiliary_version else None
|
|
|
|
if aux_version and not check_version(MAX_VERSIONS, aux_version, binary.header.machine_type):
|
|
|
|
print(f'{filename}: symbol {symbol.name} from unsupported version {version}')
|
|
|
|
ok = False
|
2020-01-22 20:32:35 +01:00
|
|
|
return ok
|
|
|
|
|
2023-05-13 17:23:21 +02:00
|
|
|
def check_exported_symbols(binary) -> bool:
|
2020-12-28 14:18:02 +01:00
|
|
|
ok: bool = True
|
2023-05-13 17:23:21 +02:00
|
|
|
|
|
|
|
for symbol in binary.dynamic_symbols:
|
|
|
|
if not symbol.exported:
|
2020-11-20 09:15:44 +01:00
|
|
|
continue
|
2023-05-13 17:23:21 +02:00
|
|
|
name = symbol.name
|
2023-05-13 17:27:38 +02:00
|
|
|
if binary.header.machine_type == lief.ELF.ARCH.RISCV or name in IGNORE_EXPORTS:
|
2020-01-22 20:32:35 +01:00
|
|
|
continue
|
2023-05-13 17:23:21 +02:00
|
|
|
print(f'{binary.name}: export of symbol {name} not allowed!')
|
2020-01-22 20:32:35 +01:00
|
|
|
ok = False
|
|
|
|
return ok
|
|
|
|
|
2023-05-13 17:23:21 +02:00
|
|
|
def check_ELF_libraries(binary) -> bool:
|
2020-12-28 14:18:02 +01:00
|
|
|
ok: bool = True
|
2023-05-13 17:23:21 +02:00
|
|
|
for library in binary.libraries:
|
|
|
|
if library not in ELF_ALLOWED_LIBRARIES:
|
|
|
|
print(f'{filename}: {library} is not in ALLOWED_LIBRARIES!')
|
2020-01-22 20:32:35 +01:00
|
|
|
ok = False
|
|
|
|
return ok
|
|
|
|
|
2023-05-13 17:23:21 +02:00
|
|
|
def check_MACHO_libraries(binary) -> bool:
|
2020-12-28 14:18:02 +01:00
|
|
|
ok: bool = True
|
2023-05-13 19:44:39 +02:00
|
|
|
for dylib in binary.libraries:
|
|
|
|
split = dylib.name.split('/')
|
|
|
|
if split[-1] not in MACHO_ALLOWED_LIBRARIES:
|
|
|
|
print(f'{split[-1]} is not in ALLOWED_LIBRARIES!')
|
2020-01-22 20:32:35 +01:00
|
|
|
ok = False
|
|
|
|
return ok
|
|
|
|
|
2023-05-13 17:23:21 +02:00
|
|
|
def check_MACHO_min_os(binary) -> bool:
|
2023-05-13 17:32:47 +02:00
|
|
|
if binary.build_version.minos == [10,15,0]:
|
|
|
|
return True
|
|
|
|
return False
|
|
|
|
|
2023-05-13 17:23:21 +02:00
|
|
|
def check_MACHO_sdk(binary) -> bool:
|
2021-05-03 08:45:08 +02:00
|
|
|
if binary.build_version.sdk == [11, 0, 0]:
|
2023-05-13 17:32:47 +02:00
|
|
|
return True
|
|
|
|
return False
|
|
|
|
|
2023-05-13 17:23:21 +02:00
|
|
|
def check_PE_libraries(binary) -> bool:
|
2020-12-28 14:18:02 +01:00
|
|
|
ok: bool = True
|
2023-05-13 19:44:39 +02:00
|
|
|
for dylib in binary.libraries:
|
2020-03-25 15:32:39 +01:00
|
|
|
if dylib not in PE_ALLOWED_LIBRARIES:
|
2023-05-13 19:44:39 +02:00
|
|
|
print(f'{dylib} is not in ALLOWED_LIBRARIES!')
|
2020-03-25 15:32:39 +01:00
|
|
|
ok = False
|
|
|
|
return ok
|
|
|
|
|
2023-05-13 17:23:21 +02:00
|
|
|
def check_PE_subsystem_version(binary) -> bool:
|
2023-05-13 17:32:47 +02:00
|
|
|
major: int = binary.optional_header.major_subsystem_version
|
|
|
|
minor: int = binary.optional_header.minor_subsystem_version
|
|
|
|
if major == 6 and minor == 1:
|
|
|
|
return True
|
|
|
|
return False
|
|
|
|
|
2021-09-28 02:26:08 +02:00
|
|
|
def check_ELF_interpreter(binary) -> bool:
|
|
|
|
expected_interpreter = ELF_INTERPRETER_NAMES[binary.header.machine_type][binary.abstract.header.endianness]
|
|
|
|
|
|
|
|
return binary.concrete.interpreter == expected_interpreter
|
|
|
|
|
2023-05-03 10:48:44 +02:00
|
|
|
def check_ELF_ABI(binary) -> bool:
|
|
|
|
expected_abi = ELF_ABIS[binary.header.machine_type][binary.abstract.header.endianness]
|
|
|
|
note = binary.concrete.get(lief.ELF.NOTE_TYPES.ABI_TAG)
|
|
|
|
assert note.details.abi == lief.ELF.NOTE_ABIS.LINUX
|
|
|
|
return note.details.version == expected_abi
|
|
|
|
|
2020-01-22 20:32:35 +01:00
|
|
|
CHECKS = {
|
2021-12-18 04:41:36 +01:00
|
|
|
lief.EXE_FORMATS.ELF: [
|
2020-01-22 20:32:35 +01:00
|
|
|
('IMPORTED_SYMBOLS', check_imported_symbols),
|
|
|
|
('EXPORTED_SYMBOLS', check_exported_symbols),
|
2021-09-28 02:26:08 +02:00
|
|
|
('LIBRARY_DEPENDENCIES', check_ELF_libraries),
|
|
|
|
('INTERPRETER_NAME', check_ELF_interpreter),
|
2023-05-03 10:48:44 +02:00
|
|
|
('ABI', check_ELF_ABI),
|
2020-01-22 20:32:35 +01:00
|
|
|
],
|
2021-12-18 04:41:36 +01:00
|
|
|
lief.EXE_FORMATS.MACHO: [
|
2023-05-13 17:32:47 +02:00
|
|
|
('DYNAMIC_LIBRARIES', check_MACHO_libraries),
|
|
|
|
('MIN_OS', check_MACHO_min_os),
|
|
|
|
('SDK', check_MACHO_sdk),
|
2020-03-25 15:32:39 +01:00
|
|
|
],
|
2021-12-18 04:41:36 +01:00
|
|
|
lief.EXE_FORMATS.PE: [
|
2023-05-13 17:32:47 +02:00
|
|
|
('DYNAMIC_LIBRARIES', check_PE_libraries),
|
|
|
|
('SUBSYSTEM_VERSION', check_PE_subsystem_version),
|
2020-01-22 20:32:35 +01:00
|
|
|
]
|
|
|
|
}
|
|
|
|
|
|
|
|
if __name__ == '__main__':
|
2020-12-28 14:18:02 +01:00
|
|
|
retval: int = 0
|
2014-04-24 17:43:22 +02:00
|
|
|
for filename in sys.argv[1:]:
|
2020-01-22 20:32:35 +01:00
|
|
|
try:
|
2023-05-13 17:23:21 +02:00
|
|
|
binary = lief.parse(filename)
|
2021-12-18 04:41:36 +01:00
|
|
|
etype = binary.format
|
2023-05-13 17:23:21 +02:00
|
|
|
if etype == lief.EXE_FORMATS.UNKNOWN:
|
|
|
|
print(f'{filename}: unknown executable format')
|
2015-10-19 14:53:56 +02:00
|
|
|
retval = 1
|
2020-01-22 20:32:35 +01:00
|
|
|
continue
|
2014-04-24 17:43:22 +02:00
|
|
|
|
2020-12-28 14:18:02 +01:00
|
|
|
failed: List[str] = []
|
2020-01-22 20:32:35 +01:00
|
|
|
for (name, func) in CHECKS[etype]:
|
2023-05-13 17:23:21 +02:00
|
|
|
if not func(binary):
|
2020-01-22 20:32:35 +01:00
|
|
|
failed.append(name)
|
|
|
|
if failed:
|
2023-05-13 19:44:39 +02:00
|
|
|
print(f'{filename}: failed {" ".join(failed)}')
|
2020-01-22 20:32:35 +01:00
|
|
|
retval = 1
|
|
|
|
except IOError:
|
2023-05-13 19:44:39 +02:00
|
|
|
print(f'{filename}: cannot open')
|
2020-01-22 20:32:35 +01:00
|
|
|
retval = 1
|
2017-08-28 22:53:34 +02:00
|
|
|
sys.exit(retval)
|