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 eitherasm()
ordisasm()
.
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 bypwntools
in theshellcraft
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.setarch='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
- 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 viadlopen
orLD_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. Usemake_elf()
if size matters. Additionally, the default value forextract
inmake_elf()
is different.Note
This is effectively a wrapper around
asm()
. with settingextract=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
- 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