Inside Bitcoin
Opening the selected file…
Inside Bitcoin
Opening the selected file…
The code behind the network, with room to understand it.
Release published 2026-10-28 · Snapshot fetched 2026-11-01. Latest stable release at fetch time; development branches and Git history are excluded. Only the selected code window and visible folders load here.
1#!/usr/bin/env python32# Copyright (c) 2015-present The Bitcoin Core developers3# Distributed under the MIT software license, see the accompanying4# file COPYING or http://www.opensource.org/licenses/mit-license.php.5'''6Perform basic security checks on a series of executables.7Exit status will be 0 if successful, and the program will be silent.8Otherwise the exit status will be 1 and it will log which executables failed which checks.10Example usage:12find ../path/to/guix/binaries -type f -executable | xargs python3 contrib/guix/security-check.py13'''14import re15import sys17import lief19def check_ELF_RELRO(binary) -> bool:20'''21Check for read-only relocations.22GNU_RELRO program header must exist23Dynamic section must have BIND_NOW flag24'''25have_gnu_relro = False26for segment in binary.segments:27# Note: not checking p_flags == PF_R: here as linkers set the permission differently28# This does not affect security: the permission flags of the GNU_RELRO program29# header are ignored, the PT_LOAD header determines the effective permissions.30# However, the dynamic linker need to write to this area so these are RW.31# Glibc itself takes care of mprotecting this area R after relocations are finished.32# See also https://marc.info/?l=binutils&m=149888335412235333if segment.type == lief.ELF.Segment.TYPE.GNU_RELRO:34have_gnu_relro = True36have_bindnow = False37try:38flags = binary.get(lief.ELF.DynamicEntry.TAG.FLAGS)39if flags.has(lief.ELF.DynamicEntryFlags.FLAG.BIND_NOW):40have_bindnow = True41except Exception:42have_bindnow = False44return have_gnu_relro and have_bindnow46def check_ELF_CANARY(binary) -> bool:47'''48Check for use of stack canary49'''50return binary.has_symbol('__stack_chk_fail')52def check_ELF_SEPARATE_CODE(binary):53'''54Check that sections are appropriately separated in virtual memory,55based on their permissions. This checks for missing -Wl,-z,separate-code56and potentially other problems.57'''58R = lief.ELF.Segment.FLAGS.R59W = lief.ELF.Segment.FLAGS.W60E = lief.ELF.Segment.FLAGS.X61EXPECTED_FLAGS = {62# Read + execute63'.init': R | E,64'.plt': R | E,65'.plt.got': R | E,66'.plt.sec': R | E,67'.text': R | E,68'.fini': R | E,69# Read-only data70'.interp': R,71'.note.gnu.property': R,72'.note.gnu.build-id': R,73'.note.ABI-tag': R,74'.gnu.hash': R,75'.dynsym': R,76'.dynstr': R,77'.gnu.version': R,78'.gnu.version_r': R,79'.rela.dyn': R,80'.rela.plt': R,81'.rodata': R,82'.eh_frame_hdr': R,83'.eh_frame': R,84'.qtmetadata': R,85'.gcc_except_table': R,86'.stapsdt.base': R,87# Writable data88'.init_array': R | W,89'.fini_array': R | W,90'.dynamic': R | W,91'.got': R | W,92'.data': R | W,93'.bss': R | W,94}95if binary.header.machine_type == lief.ELF.ARCH.PPC64:96# .plt is RW on ppc64 even with separate-code97EXPECTED_FLAGS['.plt'] = R | W98# For all LOAD program headers get mapping to the list of sections,99# and for each section, remember the flags of the associated program header.100flags_per_section = {}101for segment in binary.segments:102if segment.type == lief.ELF.Segment.TYPE.LOAD:103for section in segment.sections:104flags_per_section[section.name] = segment.flags105# Spot-check ELF LOAD program header flags per section106# If these sections exist, check them against the expected R/W/E flags107for (section, flags) in flags_per_section.items():108if section in EXPECTED_FLAGS:109if int(EXPECTED_FLAGS[section]) != int(flags):110return False111return True113def check_ELF_CONTROL_FLOW(binary) -> bool:114'''115Check for control flow instrumentation116'''117main = binary.get_function_address('main')118content = binary.get_content_from_virtual_address(main, 4, lief.Binary.VA_TYPES.AUTO)120if content.tolist() == [243, 15, 30, 250]: # endbr64121return True122return False124def check_ELF_FORTIFY(binary) -> bool:126# bitcoin wrapper does not currently contain any fortified functions127if '--monolithic' in binary.strings:128return True130chk_funcs = set()132for sym in binary.imported_symbols:133match = re.search(r'__[a-z]*_chk', sym.name)134if match:135chk_funcs.add(match.group(0))137# ignore stack-protector138chk_funcs.discard('__stack_chk')140return len(chk_funcs) >= 1142def check_PE_DYNAMIC_BASE(binary) -> bool:143'''PIE: DllCharacteristics bit 0x40 signifies dynamicbase (ASLR)'''144return lief.PE.OptionalHeader.DLL_CHARACTERISTICS.DYNAMIC_BASE in binary.optional_header.dll_characteristics_lists146# Must support high-entropy 64-bit address space layout randomization147# in addition to DYNAMIC_BASE to have secure ASLR.148def check_PE_HIGH_ENTROPY_VA(binary) -> bool:149'''PIE: DllCharacteristics bit 0x20 signifies high-entropy ASLR'''150return lief.PE.OptionalHeader.DLL_CHARACTERISTICS.HIGH_ENTROPY_VA in binary.optional_header.dll_characteristics_lists152def check_PE_RELOC_SECTION(binary) -> bool:153'''Check for a reloc section. This is required for functional ASLR.'''154return binary.has_relocations156def check_PE_CONTROL_FLOW(binary) -> bool:157'''158Check for control flow instrumentation159'''160main = binary.get_symbol('main').value162section_addr = binary.section_from_rva(main).virtual_address163virtual_address = binary.optional_header.imagebase + section_addr + main165content = binary.get_content_from_virtual_address(virtual_address, 4, lief.Binary.VA_TYPES.VA)167if content.tolist() == [243, 15, 30, 250]: # endbr64168return True169return False171def check_PE_CANARY(binary) -> bool:172'''173Check for use of stack canary174'''175return binary.has_symbol('__stack_chk_fail')177def check_MACHO_NOUNDEFS(binary) -> bool:178'''179Check for no undefined references.180'''181return binary.header.has(lief.MachO.Header.FLAGS.NOUNDEFS)183def check_MACHO_FIXUP_CHAINS(binary) -> bool:184'''185Check for use of chained fixups.186'''187return binary.has_dyld_chained_fixups189def check_MACHO_CANARY(binary) -> bool:190'''191Check for use of stack canary192'''193return binary.has_symbol('___stack_chk_fail')195def check_PIE(binary) -> bool:196'''197Check for position independent executable (PIE),198allowing for address space randomization.199'''200return binary.is_pie202def check_NX(binary) -> bool:203'''204Check for no stack execution205'''207# binary.has_nx checks are only for the stack, but MachO binaries might208# have executable heaps.209if binary.format == lief.Binary.FORMATS.MACHO:210return binary.concrete.has_nx_stack and binary.concrete.has_nx_heap211else:212return binary.has_nx214def check_MACHO_CONTROL_FLOW(binary) -> bool:215'''216Check for control flow instrumentation217'''218content = binary.get_content_from_virtual_address(binary.entrypoint, 4, lief.Binary.VA_TYPES.AUTO)220if content.tolist() == [243, 15, 30, 250]: # endbr64221return True222return False224def check_MACHO_BRANCH_PROTECTION(binary) -> bool:225'''226Check for branch protection instrumentation227'''228content = binary.get_content_from_virtual_address(binary.entrypoint, 4, lief.Binary.VA_TYPES.AUTO)230if content.tolist() == [95, 36, 3, 213]: # bti231return True232return False234BASE_ELF = [235('FORTIFY', check_ELF_FORTIFY),236('PIE', check_PIE),237('NX', check_NX),238('RELRO', check_ELF_RELRO),239('CANARY', check_ELF_CANARY),240('SEPARATE_CODE', check_ELF_SEPARATE_CODE),241]243BASE_PE = [244('PIE', check_PIE),245('DYNAMIC_BASE', check_PE_DYNAMIC_BASE),246('HIGH_ENTROPY_VA', check_PE_HIGH_ENTROPY_VA),247('NX', check_NX),248('RELOC_SECTION', check_PE_RELOC_SECTION),249('CONTROL_FLOW', check_PE_CONTROL_FLOW),250('CANARY', check_PE_CANARY),
Independent explanations by MyBitcoinExplorer. Not affiliated with or endorsed by Bitcoin Core. Guides are educational and do not replace the source or its tests.
Bitcoin Core source © its contributors, distributed under the MIT license. Original notices and bundled third-party licenses are retained in the source tree.