pwnlib.asm — Assembler functions

Utilities for assembling and disassembling code.

Architecture Selection

Architecture, endianness, and word size are selected by using pwnlib.context.

Any parameters which can be specified to context can also be specified as keyword arguments to either asm() or disasm().

Assembly

To assemble code, simply invoke asm() on the code to assemble.

>>> asm('mov eax, 0')
b'\xb8\x00\x00\x00\x00'

Additionally, you can use constants as defined in the pwnlib.constants module.

>>> asm('mov eax, SYS_execve')
b'\xb8\x0b\x00\x00\x00'

Finally, asm() is used to assemble shellcode provided by pwntools in the shellcraft module.

>>> asm(shellcraft.nop())
b'\x90'

Disassembly

To disassemble code, simply invoke disasm() on the bytes to disassemble.

>>> disasm(b'\xb8\x0b\x00\x00\x00')
'   0:   b8 0b 00 00 00          mov    eax, 0xb'
pwnlib.asm.asm(code, vma = 0, extract = True, shared = False, ...) str[source]

Runs cpp() over a given shellcode and then assembles it into bytes.

To see which architectures or operating systems are supported, look in pwnlib.context.

Assembling shellcode requires that the GNU assembler is installed for the target architecture. See Installing Binutils for more information.

Parameters
  • shellcode (str) – Assembler code to assemble.

  • vma (int) – Virtual memory address of the beginning of assembly

  • extract (bool) – Extract the raw assembly bytes from the assembled file. If False, returns the path to an ELF file with the assembly embedded.

  • shared (bool) – Create a shared object.

  • kwargs (dict) – Any attributes on context can be set, e.g.set arch='arm'.

Examples

>>> asm("mov eax, SYS_select", arch = 'i386', os = 'freebsd')
b'\xb8]\x00\x00\x00'
>>> asm("mov eax, SYS_select", arch = 'amd64', os = 'linux')
b'\xb8\x17\x00\x00\x00'
>>> asm("mov rax, SYS_select", arch = 'amd64', os = 'linux')
b'H\xc7\xc0\x17\x00\x00\x00'
>>> asm("mov r0, #SYS_select", arch = 'arm', os = 'linux', bits=32)
b'R\x00\xa0\xe3'
>>> asm("mov #42, r0", arch = 'msp430')
b'0@*\x00'
>>> asm("la %r0, 42", arch = 's390', bits=64)
b'A\x00\x00*'
pwnlib.asm.cpp(shellcode, ...) str[source]

Runs CPP over the given shellcode.

The output will always contain exactly one newline at the end.

Parameters

shellcode (str) – Shellcode to preprocess

Kwargs:

Any arguments/properties that can be set on context

Examples

>>> cpp("mov al, SYS_setresuid", arch = "i386", os = "linux")
'mov al, 164\n'
>>> cpp("weee SYS_setresuid", arch = "arm", os = "linux")
'weee (0+164)\n'
>>> cpp("SYS_setresuid", arch = "thumb", os = "linux")
'(0+164)\n'
>>> cpp("SYS_setresuid", os = "freebsd")
'311\n'
pwnlib.asm.disasm(data, ...) str[source]

Disassembles a bytestring into human readable assembler.

To see which architectures are supported, look in pwnlib.contex.

Parameters
  • data (str) – Bytestring to disassemble.

  • vma (int) – Passed through to the –adjust-vma argument of objdump

  • byte (bool) – Include the hex-printed bytes in the disassembly

  • offset (bool) – Include the virtual memory address in the disassembly

Kwargs:

Any arguments/properties that can be set on context

Examples

>>> print(disasm(unhex('b85d000000'), arch = 'i386'))
   0:   b8 5d 00 00 00          mov    eax, 0x5d
>>> print(disasm(unhex('b85d000000'), arch = 'i386', byte = 0))
   0:   mov    eax, 0x5d
>>> print(disasm(unhex('b85d000000'), arch = 'i386', byte = 0, offset = 0))
mov    eax, 0x5d
>>> print(disasm(unhex('b817000000'), arch = 'amd64'))
   0:   b8 17 00 00 00          mov    eax, 0x17
>>> print(disasm(unhex('48c7c017000000'), arch = 'amd64'))
   0:   48 c7 c0 17 00 00 00    mov    rax, 0x17
>>> print(disasm(unhex('04001fe552009000'), arch = 'arm'))
   0:   e51f0004        ldr     r0, [pc, #-4]   ; 0x4
   4:   00900052        addseq  r0, r0, r2, asr r0
>>> print(disasm(unhex('4ff00500'), arch = 'thumb', bits=32))
   0:   f04f 0005       mov.w   r0, #5
>>> print(disasm(unhex('656664676665400F18A4000000000051'), byte=0, arch='amd64'))
   0:   gs data16 fs rex nop WORD PTR gs:[eax+eax*1+0x0]
   f:   push   rcx
>>> print(disasm(unhex('01000000'), arch='sparc64'))
   0:   01 00 00 00     nop
>>> print(disasm(unhex('60000000'), arch='powerpc64'))
   0:   60 00 00 00     nop
>>> print(disasm(unhex('00000000'), arch='mips64'))
   0:   00000000        nop
>>> print(disasm(unhex('48b84141414141414100c3'), arch='amd64'))
   0:   48 b8 41 41 41 41 41 41 41 00   movabs rax, 0x41414141414141
   a:   c3                      ret
>>> print(disasm(unhex('00000000'), vma=0x80000000, arch='mips'))
80000000:       00000000        nop
pwnlib.asm.make_elf(data, vma=None, strip=True, extract=True, shared=False, **kwargs) str[source]

Builds an ELF file with the specified binary data as its executable code.

Parameters
  • data (str) – Assembled code

  • vma (int) – Load address for the ELF file

  • strip (bool) – Strip the resulting ELF file. Only matters if extract=False. (Default: True)

  • extract (bool) – Extract the assembly from the ELF file. If False, the path of the ELF file is returned. (Default: True)

  • shared (bool) – Create a Dynamic Shared Object (DSO, i.e. a .so) which can be loaded via dlopen or LD_PRELOAD.

Examples

This example creates an i386 ELF that just does execve(‘/bin/sh’,…).

>>> context.clear(arch='i386')
>>> bin_sh = unhex('6a68682f2f2f73682f62696e89e331c96a0b5899cd80')
>>> filename = make_elf(bin_sh, extract=False)
>>> p = process(filename)
>>> p.sendline(b'echo Hello; exit')
>>> p.recvline()
b'Hello\n'
pwnlib.asm.make_elf_from_assembly(assembly, vma=None, extract=None, shared=False, strip=False, **kwargs) str[source]

Builds an ELF file with the specified assembly as its executable code.

This differs from make_elf() in that all ELF symbols are preserved, such as labels and local variables. Use make_elf() if size matters. Additionally, the default value for extract in make_elf() is different.

Note

This is effectively a wrapper around asm(). with setting extract=False, vma=0x10000000, and marking the resulting file as executable (chmod +x).

Note

ELF files created with arch=thumb will prepend an ARM stub which switches to Thumb mode.

Parameters
  • assembly (str) – Assembly code to build into an ELF

  • vma (int) – Load address of the binary (Default: 0x10000000, or 0 if shared=True)

  • extract (bool) – Extract the full ELF data from the file. (Default: False)

  • shared (bool) – Create a shared library (Default: False)

  • kwargs (dict) – Arguments to pass to asm().

Returns

The path to the assembled ELF (extract=False), or the data of the assembled ELF.

Example

This example shows how to create a shared library, and load it via LD_PRELOAD.

>>> context.clear()
>>> context.arch = 'amd64'
>>> sc = 'push rbp; mov rbp, rsp;'
>>> sc += shellcraft.echo('Hello\n')
>>> sc += 'mov rsp, rbp; pop rbp; ret'
>>> solib = make_elf_from_assembly(sc, shared=1)
>>> subprocess.check_output(['echo', 'World'], env={'LD_PRELOAD': solib}, universal_newlines = True)
'Hello\nWorld\n'

The same thing can be done with make_elf(), though the sizes are different. They both

>>> file_a = make_elf(asm('nop'), extract=True)
>>> file_b = make_elf_from_assembly('nop', extract=True)
>>> file_a[:4] == file_b[:4]
True
>>> len(file_a) < len(file_b)
True

Internal Functions

These are only included so that their tests are run.

You should never need these.

pwnlib.asm.dpkg_search_for_binutils(arch, util)[source]

Use dpkg to search for any available assemblers which will work.

Returns

A list of candidate package names.

>>> pwnlib.asm.dpkg_search_for_binutils('aarch64', 'as')
['binutils-aarch64-linux-gnu']
pwnlib.asm.print_binutils_instructions(util, context)[source]

On failure to find a binutils utility, inform the user of a way they can get it easily.

Doctest:

>>> context.clear(arch = 'amd64')
>>> pwnlib.asm.print_binutils_instructions('as', context)
Traceback (most recent call last):
...
PwnlibException: Could not find 'as' installed for ContextType(arch = 'amd64', bits = 64, endian = 'little')
Try installing binutils for this architecture:
$ sudo apt-get install binutils