Skip to main content

Boron is a hobby project that implements an x86-64 assembler in Python.

Project description

BORON Assembler

UNDER DEVELOPMENT - TRY IT AT YOUR OWN RISK

What is Boron?

Boron is a hobby project that implements an x86-64 assembler in Python.
You can dynamically assemble instructions at runtime and execute them immediately.

Why Boron?

  • Live machine code generation: Assemble instructions directly in Python and execute them on the fly.
  • Full control: Every instruction, operand, and offset is manipulable from Python objects.
  • Learn by doing: Experiment with instructions, syscalls, and memory layout without writing intermediate files.
  • Transparent and hackable: More comments and documentation will be added soon to help you understand the inner workings.

Unlike NASM or GCC assemblers, Boron lets you experiment and run machine code directly in Python. Multi-file projects or libraries like libc still require the system linker, but single-file programs can be assembled and executed dynamically (soon).

Limitations

  • No built-in runtime execution yet: You have to handle relocations manually.
  • Relocatable ELF32/ELF64 files only: COFF format and other outputs are not supported yet.
  • Under active development: Bugs and breaking changes are expected.
  • Not commented yet: I will add comments to the code.

Next steps

Current Goals

  • Built-in runtime execution: I will add an class for it.
  • More instructions: I will add more x64 instructions. Currently Boron have a few instructions (24)
  • Documentation: I will fill the documentation folder and comment the code.

Future Goals

  • Other architecthtures: I will add x16/x86 architecthure
  • More file formats: Like coff
  • Disassembler: From flat binary
  • Improvement of the readme: I will add more examples.
  • And others

How to use Boron?

Simple "Hello, World!"

import mmap, ctypes
from boron.assembler.x64.General import instructions, operands
from boron.assembler.x64.General.registers import GPRegisters

# Message
hello = b"Hello, world!\n"
hello_len = len(hello)
hello_buf = ctypes.create_string_buffer(hello)  # buffer in memory
buf_addr = ctypes.addressof(hello_buf)         # raw byte address

# Assembly program
program = [
    # write(1, buf, len)
    instructions.INSTRUCTIONS.MOV.R_IMM(GPRegisters.rax, operands.Immediate(1, 8)),  # sys_write
    instructions.INSTRUCTIONS.MOV.R_IMM(GPRegisters.rdi, operands.Immediate(1, 8)),  # stdout
    instructions.INSTRUCTIONS.MOV.R_IMM(GPRegisters.rsi, operands.Immediate(buf_addr, 8)),
    instructions.INSTRUCTIONS.MOV.R_IMM(GPRegisters.rdx, operands.Immediate(hello_len, 8)),
    instructions.INSTRUCTIONS.SYSCALL(),

    # exit(0)
    instructions.INSTRUCTIONS.MOV.R_IMM(GPRegisters.rax, operands.Immediate(60, 8)),  
    instructions.INSTRUCTIONS.XOR.R_R(GPRegisters.rdi, GPRegisters.rdi),
    instructions.INSTRUCTIONS.SYSCALL(),
]

# Assemble to machine code
code = b""
for instr in program:
    for i in instr.emit():
        code += i.emit()

# Allocate executable memory and copy code
mem = mmap.mmap(-1, len(code), prot=mmap.PROT_READ | mmap.PROT_WRITE | mmap.PROT_EXEC,
                flags=mmap.MAP_PRIVATE | mmap.MAP_ANONYMOUS)
mem.write(code)

# Execute
addr = ctypes.c_void_p.from_buffer(mem)
entry = ctypes.CFUNCTYPE(None)(ctypes.addressof(addr))
entry()

Prints the string in hello directly. No NASM or linker required. Works on Linux.

And yes that was for just a "Hello, World!"

simple "Hello, World!" but as a file

from boron.codegen.builder import Builder
from boron.assembler.assembler import x64,ASSEMBLER,SECTION,ARCH,FILE
from boron.codegen.file import elf
from boron.codegen.section import SectionKind, SectionFlags, SymbolBinding
#required modules in boron

builder = ASSEMBLER(ARCH.x64,True)
msg = b"Hello, World!\n"
# TEXT SECTION
text = builder.add_section(
    SECTION(".text", SectionKind.CODE, SectionFlags.EXEC, alignment=0x1000)
)


a = text.add_label("_start", binding=SymbolBinding.GLOBAL)

# write syscall
# rax = 1 (sys_write)
text.add(x64.General.instructions.INSTRUCTIONS.MOV.R_IMM(
    x64.General.registers.GPRegisters.eax,
    x64.General.operands.Immediate(1,4)
))

# rdi = 1 (stdout)
text.add(x64.General.instructions.INSTRUCTIONS.MOV.R_IMM(
    x64.General.registers.GPRegisters.edi,
    x64.General.operands.Immediate(1,4)
))

# rsi = &msg
text.add(x64.General.instructions.INSTRUCTIONS.MOV.R_IMM(
    x64.General.registers.GPRegisters.rsi,
    x64.General.operands.SYMBOL("msg", 8, False, 0))
)
# rdx = len
text.add(x64.General.instructions.INSTRUCTIONS.MOV.R_IMM(
    x64.General.registers.GPRegisters.edx,
    x64.General.operands.Immediate(len(msg),4)  # "Hello, world!\n"
))

b = text.add(x64.General.instructions.INSTRUCTIONS.SYSCALL())


# exit syscall
# rax = 60
text.add(x64.General.instructions.INSTRUCTIONS.MOV.R_IMM(
    x64.General.registers.GPRegisters.rax,
    x64.General.operands.Immediate(60,4)
))

# rdi = 0
text.add(x64.General.instructions.INSTRUCTIONS.MOV.R_IMM(
    x64.General.registers.GPRegisters.rdi,
    x64.General.operands.Immediate(0,4)
))

text.add(x64.General.instructions.INSTRUCTIONS.SYSCALL())


# DATA SECTION
data = builder.add_section(
    SECTION(".data", SectionKind.DATA,
            SectionFlags.READ | SectionFlags.WRITE,
            alignment=0x10)
)
data.add_label("msg", binding=SymbolBinding.LOCAL)
data.db(msg)

# WRITE FILE
with open("out", "wb") as f:
    out = builder.build(FILE.ELF)
    f.write(out)

It may be require admin permissions (sudo) you need to link it with ld out -o hello

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

boron_assembler-0.1.0.tar.gz (4.4 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

boron_assembler-0.1.0-py3-none-any.whl (4.3 kB view details)

Uploaded Python 3

File details

Details for the file boron_assembler-0.1.0.tar.gz.

File metadata

  • Download URL: boron_assembler-0.1.0.tar.gz
  • Upload date:
  • Size: 4.4 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for boron_assembler-0.1.0.tar.gz
Algorithm Hash digest
SHA256 c49200cc67771097c8362aea1971a6a414763f5cc14c33162c2afdaa40641835
MD5 1be5f7fea8561ad375f9b781308ef1a4
BLAKE2b-256 2cbda28dd1b388dda42d5c5ab1a9e4bcb3a17caed80fdaa54c60b7da29833ac2

See more details on using hashes here.

Provenance

The following attestation bundles were made for boron_assembler-0.1.0.tar.gz:

Publisher: python-package.yml on TAN-JANT/BORON

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file boron_assembler-0.1.0-py3-none-any.whl.

File metadata

File hashes

Hashes for boron_assembler-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 d884f59738689b7132dd226254025a9e248142851d8d4a3600bb5548ebaa7d60
MD5 89ff828b870ce1dd27ee3dcc2ad73f4f
BLAKE2b-256 8d8d20a61848ea4e3ecf43bdcdcb34ab887eef9b7c95c43d61bede36505e482b

See more details on using hashes here.

Provenance

The following attestation bundles were made for boron_assembler-0.1.0-py3-none-any.whl:

Publisher: python-package.yml on TAN-JANT/BORON

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page