DedSec Project

Executable Formats, Loaders and Dynamic Linking — DedSec Project

Executable Formats, Loaders and Dynamic Linking Purpose: Understand how operating systems transform executable files into running processes and how imports, relocations, shared libraries…

Purpose: Understand how operating systems transform executable files into running processes and how imports, relocations, shared libraries, and loader policy affect security analysis.

Learning objectives

From source to process

Source code is compiled or interpreted into forms the runtime can execute. For native code, the path commonly includes compilation into object files, linking into an executable or shared library, storage in a platform file format, and loading/mapping by the operating system. Dynamic dependencies may be resolved at process start or lazily later.

Each stage introduces metadata that is valuable to an analyst: symbols, relocation records, import tables, section permissions, debug information, build IDs, dependencies, and hardening flags.

ELF mental model

ELF is common on Linux, Android native code, and many Unix-like systems. It contains headers describing the file and program loading. Sections organize link-time information such as code, data, symbols, and relocations. Program headers/segments tell the loader what to map into memory and with what permissions.

A frequent beginner error is treating sections and segments as identical. A stripped production binary can still be loaded even when much link-time naming information is absent because the loader primarily follows program headers.

PE/COFF mental model

Windows executables and DLLs use the PE/COFF family. Relevant structures include DOS/PE headers, section table, import/export data, relocation information, resources, debug directories, and optional-header fields describing image layout and security characteristics.

For security analysis, imports often provide behavioral clues: networking, cryptography, process creation, registry access, or file operations. Imports are evidence, not proof—a program can resolve functions dynamically or contain unused imports.

Static versus dynamic linking

Static linking copies required library code into the final binary. Dynamic linking leaves dependencies to shared libraries/DLLs that are resolved at runtime. Dynamic linking reduces duplication and supports centralized library updates but creates dependency-resolution and search-path considerations.

Security questions include:

Study these questions defensively on software you own; do not attempt library hijacking on third-party systems.

Imports, exports, and symbols

An import is an external symbol a module expects another component to provide. An export is a symbol made available to others. Symbol tables may include function and variable names; stripped binaries remove much human-friendly naming but must retain whatever runtime resolution still requires.

C++ and other languages may mangle names to encode type information. Demangling can recover readable signatures. Debug symbols can provide rich type and source mappings when available.

Relocations and ASLR

Code and data often contain references that depend on where the image is loaded. Relocations describe places that need adjustment. Position-independent code minimizes fixed-address assumptions and supports address randomization.

On ELF systems, concepts such as the GOT and PLT participate in dynamic symbol resolution. On Windows, import-address tables play a related role. Learn the architecture as a data-flow mechanism rather than memorizing offensive techniques around it.

W^X and memory permissions

A strong design goal is that memory should not be writable and executable at the same time unless absolutely required. Loaders map code and data with different permissions. Inspecting segment/section flags reveals whether an image follows expected separation.

The loader may also enforce stack execute policy, relocation protections, control-flow features, signatures, or platform entitlements depending on the OS.

Initialization and entry points

Execution does not always begin directly at main. Runtime startup code initializes the process, dynamic loader, thread-local storage, constructors, language runtime, and environment before application logic runs. Reverse engineers therefore distinguish the image entry point from the high-level program entry function.

Likewise, shared libraries can have initialization routines. Security review should consider initialization order because privileged state or environment may exist before normal application checks.

File hashing and provenance

When analyzing a binary, record a cryptographic hash, file size, architecture, timestamp metadata, source/provenance if known, and tool versions. Hashes identify exact bytes; they do not prove trustworthiness. Signing and reproducible builds provide additional provenance signals.

Safe ELF lab

Compile the benign program from Module 63. Then inspect it:

file ./demo
readelf -h ./demo
readelf -l ./demo
readelf -S ./demo
readelf -d ./demo

If available:

objdump -p ./demo
nm ./demo | head

Write down architecture, entry point, loadable segments, dependencies, and which regions are executable versus writable.

Safe Windows/PE lab

On a Windows VM, compile a hello-world application you own. Use platform tools such as dumpbin /headers when available, or a trusted PE-inspection utility in read-only mode. Identify sections, imports, architecture, and security-related characteristics. Do not modify system binaries.

Loader security review

For an application you maintain, review:

Android native libraries

Android packages can contain native .so libraries for one or more ABIs. The application sandbox still applies, while native code introduces memory-safety and JNI boundary concerns. APK signing establishes package/update identity, while ELF hardening affects native components separately.

Guided study workflow

Before you begin

Complete Modules 53, 62, and 63.

Practice task

Compile one tiny program and one shared library you own. Inspect headers, dependencies, symbols, and memory permission layout. Draw the path from file bytes to mapped process segments.

Evidence to keep

Keep hashes, tool output excerpts, a loader diagram, and a brief hardening inventory.

Common mistakes to avoid

Mastery check

Explain how a dynamic dependency becomes mapped code, how ASLR changes load addresses, and where loader trust decisions can fail.

Continue with

Modules 65, 66, 67, 73, 79, and 82.