Pwntools makes it very easy to perform assembly in almost any architecture, and comes with a wide variety of canned-but-customizable shellcode ready to go out-of-the-box.
In the walkthrough directory, there are several longer shellcode tutorials. This page gives you the basics.
The most basic example, is to convert assembly into shellcode.
from pwn import *
print repr(asm('xor edi, edi'))
# '1\xff'
print enhex(asm('xor edi, edi'))
# 31ffThe shellcraft module gives you pre-canned assembly. It is generally customizable. The easiest way to find out which shellcraft templates exist is to look at the documentation on RTD.
from pwn import *
help(shellcraft.sh)
print '---'
print shellcraft.sh()
print '---'
print enhex(asm(shellcraft.sh()))Help on function sh in module pwnlib.shellcraft.internal:
sh()
Execute /bin/sh
---
/* push '/bin///sh\x00' */
push 0x68
push 0x732f2f2f
push 0x6e69622f
/* call execve('esp', 0, 0) */
push (SYS_execve) /* 0xb */
pop eax
mov ebx, esp
xor ecx, ecx
cdq /* edx=0 */
int 0x80
---
6a68682f2f2f73682f62696e6a0b5889e331c999cd80
There are three command-line tools for interacting with assembly:
asmdisasmshellcraft
The asm tool does what it says on the tin. It provides several options for formatting the output. When the output is a terminal, it defaults to hex-encoded.
$ asm nop
90
When the output is anything else, it writes the raw data.
$ asm nop | xxd
0000000: 90 .
It also takes data on stdin if no instructions are provided on the command line.
$ echo 'push ebx; pop edi' | asm
535f
Finally, it supports a few different options for specifying the output format, via the --format option. Supported arguments are raw, hex, string, and elf.
$ asm --format=elf 'int3' > ./int3
$ ./halt
Trace/breakpoint trap (core dumped)
Disasm is the opposite of asm.
$ disasm cd80
0: cd 80 int 0x80
$ asm nop | disasm
0: 90 nop
The shellcraft command is the command-line interface to the internal shellcraft module. On the command-line, the full context must be specified, in the order of arch.os.template.
$ shellcraft i386.linux.sh
6a68682f2f2f73682f62696e6a0b5889e331c999cd80
Assembling for a foreign architecture requires that you have an appropriate version of binutils installed. You should see installing.md for more information on this. The only change that is necessary is to set the architecture in the global context variable. You can see more information about context in context.md.
from pwn import *
context.arch = 'arm'
print repr(asm('mov r0, r1'))
# '\x01\x00\xa0\xe1'
print enhex(asm('mov r0, r1'))
# 0100a0e1The shellcraft module automatically switches to the appropriate architecture.
from pwn import *
context.arch = 'arm'
print shellcraft.sh()
print enhex(asm(shellcraft.sh())) adr r0, bin_sh
mov r2, #0
mov r1, r2
svc SYS_execve
bin_sh: .asciz "/bin/sh"
08008fe20020a0e30210a0e10b0000ef2f62696e2f736800
You can also use the command line to assemble foreign-arch shellcode, by using the --context command-line option.
$ asm --context=arm 'mov r0, r1'
0100a0e1
$ shellcraft arm.linux.sh
08008fe20020a0e30210a0e10b0000ef2f62696e2f736800