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'))
# 31ff
The 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:
asm
disasm
shellcraft
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'))
# 0100a0e1
The 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