Guest User

Untitled

a guest
Feb 21st, 2025
223
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 6.44 KB | None | 0 0
  1. #!/usr/bin/env python3
  2. import struct
  3. from typing import Final
  4.  
  5. def build_message() -> bytes:
  6.     """
  7.    Build the data segment containing the message.
  8.    """
  9.     return b"Hello, world\n"  # 13 bytes
  10.  
  11. def build_code(message_disp: int) -> bytes:
  12.     """
  13.    Build the machine-code segment that performs:
  14.      - write syscall to print the message, and
  15.      - exit syscall to terminate the program.
  16.    
  17.    The instruction 'lea rsi, [rip+disp]' will use the provided
  18.    displacement (in bytes) to compute the address of the message.
  19.    """
  20.     # Assemble the instructions:
  21.     #   mov rax, 1              ; syscall number for write
  22.     #   mov rdi, 1              ; file descriptor (stdout)
  23.     #   lea rsi, [rip+disp]     ; pointer to the message (disp provided)
  24.     #   mov rdx, 13             ; length of the message
  25.     #   syscall                 ; invoke write
  26.     #   mov rax, 60             ; syscall number for exit
  27.     #   xor rdi, rdi            ; exit code 0
  28.     #   syscall                 ; invoke exit
  29.     code_bytes: Final[bytes] = bytes([
  30.         # mov rax, 1
  31.         0x48, 0xc7, 0xc0, 0x01, 0x00, 0x00, 0x00,
  32.         # mov rdi, 1
  33.         0x48, 0xc7, 0xc7, 0x01, 0x00, 0x00, 0x00,
  34.         # lea rsi, [rip+disp]
  35.         0x48, 0x8d, 0x35,
  36.         # disp (4 bytes placeholder, will be replaced below)
  37.         0x00, 0x00, 0x00, 0x00,
  38.         # mov rdx, 13
  39.         0x48, 0xc7, 0xc2, 0x0d, 0x00, 0x00, 0x00,
  40.         # syscall (write)
  41.         0x0f, 0x05,
  42.         # mov rax, 60
  43.         0x48, 0xc7, 0xc0, 0x3c, 0x00, 0x00, 0x00,
  44.         # xor rdi, rdi
  45.         0x48, 0x31, 0xff,
  46.         # syscall (exit)
  47.         0x0f, 0x05
  48.     ])
  49.     # The lea instruction starts at offset 14 and occupies 7 bytes.
  50.     # The displacement is stored in 4 bytes at offset 17.
  51.     # The effective address for the message will be computed relative
  52.     # to the address immediately after the lea instruction (offset 21).
  53.     disp_bytes: bytes = struct.pack("<i", message_disp)
  54.     code_array = bytearray(code_bytes)
  55.     code_array[17:21] = disp_bytes
  56.     return bytes(code_array)
  57.  
  58. def build_segment() -> bytes:
  59.     """
  60.    Construct the full segment (code followed by data).
  61.    
  62.    The correct displacement in the lea instruction must be the difference
  63.    between the start of the data and the address following the lea instruction.
  64.    """
  65.     message: bytes = build_message()
  66.     # First, build the code with a dummy displacement.
  67.     dummy_code = build_code(0)
  68.     # The lea instruction's effective address is computed from offset 21.
  69.     # The correct displacement is:
  70.     #     len(code) + 0 (start of message) - 21 = len(code) - 21
  71.     correct_disp: int = len(dummy_code) - 21
  72.     code: bytes = build_code(correct_disp)
  73.     return code + message
  74.  
  75. def build_elf_header(segment_offset: int, entry_point: int, ph_offset: int, total_size: int) -> bytes:
  76.     """
  77.    Build the ELF header (64 bytes) with the given parameters.
  78.    
  79.    - segment_offset: Offset in the file where the code+data segment begins.
  80.    - entry_point: Virtual address at which execution begins.
  81.    - ph_offset: Offset to the program header table (immediately after ELF header).
  82.    - total_size: Total file size (used for segment size fields).
  83.    """
  84.     # e_ident: magic number, 64-bit, little-endian, version 1, etc.
  85.     e_ident: bytes = b'\x7fELF' + bytes([2, 1, 1, 0]) + bytes(8)
  86.     e_type: int = 2            # Executable file (ET_EXEC)
  87.     e_machine: int = 0x3e        # x86-64 (EM_X86_64)
  88.     e_version: int = 1
  89.     e_entry: int = entry_point  # Entry point virtual address
  90.     e_phoff: int = ph_offset    # Program header table offset
  91.     e_shoff: int = 0            # No section header table in this minimal binary
  92.     e_flags: int = 0
  93.     e_ehsize: int = 64          # ELF header size
  94.     e_phentsize: int = 56       # Program header entry size for ELF64 is 56 bytes
  95.     e_phnum: int = 1            # One program header entry
  96.     e_shentsize: int = 0        # Not used
  97.     e_shnum: int = 0            # Not used
  98.     e_shstrndx: int = 0         # Not used
  99.  
  100.     return struct.pack(
  101.         "<16sHHIQQQIHHHHHH",
  102.         e_ident,
  103.         e_type,
  104.         e_machine,
  105.         e_version,
  106.         e_entry,
  107.         e_phoff,
  108.         e_shoff,
  109.         e_flags,
  110.         e_ehsize,
  111.         e_phentsize,
  112.         e_phnum,
  113.         e_shentsize,
  114.         e_shnum,
  115.         e_shstrndx
  116.     )
  117.  
  118. def build_program_header(total_size: int, base_vaddr: int) -> bytes:
  119.     """
  120.    Build the program header (PT_LOAD) that maps the entire file into memory.
  121.    
  122.    - total_size: Total file size (headers + segment).
  123.    - base_vaddr: Base virtual address at which the segment is loaded.
  124.    """
  125.     p_type: int = 1          # PT_LOAD
  126.     p_flags: int = 5         # PF_R | PF_X (readable and executable)
  127.     p_offset: int = 0        # Segment starts at file offset 0
  128.     p_vaddr: int = base_vaddr   # Virtual address where the segment is loaded
  129.     p_paddr: int = base_vaddr   # Physical address (unused on Linux)
  130.     p_filesz: int = total_size
  131.     p_memsz: int = total_size
  132.     p_align: int = 0x200000    # Typical page alignment
  133.  
  134.     return struct.pack(
  135.         "<IIQQQQQQ",
  136.         p_type,
  137.         p_flags,
  138.         p_offset,
  139.         p_vaddr,
  140.         p_paddr,
  141.         p_filesz,
  142.         p_memsz,
  143.         p_align
  144.     )
  145.  
  146. def create_elf() -> bytes:
  147.     """
  148.    Assemble the final ELF binary by combining:
  149.      - the ELF header,
  150.      - the program header, and
  151.      - the segment (code + data).
  152.    """
  153.     base_vaddr: Final[int] = 0x400000
  154.     elf_header_size: int = 64
  155.     ph_size: int = 56
  156.     segment_offset: int = elf_header_size + ph_size
  157.     segment: bytes = build_segment()
  158.     total_size: int = segment_offset + len(segment)
  159.     entry_point: int = base_vaddr + segment_offset
  160.  
  161.     elf_hdr: bytes = build_elf_header(segment_offset, entry_point, elf_header_size, total_size)
  162.     prog_hdr: bytes = build_program_header(total_size, base_vaddr)
  163.     return elf_hdr + prog_hdr + segment
  164.  
  165. def write_elf(file_name: str, elf_binary: bytes) -> None:
  166.     """
  167.    Write the ELF binary to a file.
  168.    """
  169.     with open(file_name, "wb") as f:
  170.         f.write(elf_binary)
  171.  
  172. def main() -> None:
  173.     elf_binary: bytes = create_elf()
  174.     file_name: str = "hello"
  175.     write_elf(file_name, elf_binary)
  176.     print(f"ELF binary written to {file_name}.")
  177.     print("Mark it executable (e.g., 'chmod +x hello') and run it on a Linux x86_64 system.")
  178.  
  179. if __name__ == "__main__":
  180.     main()
  181.  
Advertisement
Add Comment
Please, Sign In to add comment