Home

Awesome

PyShellCode

Execute ShellCode in Python. In other words, allows to use "inline assembler" in Python.

There are three implementations:

All the code is highly platform-dependent. In my case: Linux thinkpad 4.8.0-2-amd64 #1 SMP Debian 4.8.11-1 (2016-12-02) x86_64 GNU/Linux

This is intended as a simple project to learn more about x86-64 assembler and linux (see paragraph Other Work) As well as serve for a easy platform to recreate cache-attacks and other side-channels attacks which require assembly. If you want to use the code to do something similar I recommend reading the paragraph Technical Details below and implement it yourself (or copy&paste Simple.py) since it's not hard once you know how-to do it.

Contains also some interesting code for CMake, to build shared-libraries which are executable and take command line arguments.

Installation

and/or (latter if you only require the Python version)

Technical Details

At first we require some shell-code (a binary string containing the correct opcodes which can directly be executed by your CPU). One can create such a string by hand, or use an assembler like nasm to create it for you:

For instance, create a file shellcode.asm

global _start
section .text
_start:
    <Your Assembly Code goes here>

and execute:

nasm -f elf64 shellcode.asm -o shellcode.o
ld -o shellcode shellcode.o
for i in `objdump -d shellcode | tr '\t' ' ' | tr ' ' '\n' | egrep '^[0-9a-f]{2}$' ` ; do echo -n "\x$i" ; done

The function create_shellcode_from_nasmcode in Simple.py does this for you.

A simple hello world shell-code looks like this

shell_code = b"\xeb\x13\xb8\x01\x00\x00\x00\xbf\x01\x00\x00\x00\x5e\xba\x0f\x00\x00\x00\x0f\x05\xc3\xe8\xe8\xff\xff\xff\x48\x65\x6c\x6c\x6f\x2c\x20\x57\x6f\x72\x6c\x64\x21\x0a"

Side note: Traditional shell-code is concerned with avoiding 0-bytes, because it is typically injected into a unvalided user-input request. This isn't a problem if you just want to execute your code as inline-assembler, because python-strings can contain 0-bytes.

Secondly we create a pice of executable memory in python and write our shell-code into this memory

import mmap
shell_code = b'<Your Shell Code goes here>'
mm = mmap.mmap(-1, len(shell_code), flags=mmap.MAP_SHARED | mmap.MAP_ANONYMOUS, prot=mmap.PROT_WRITE | mmap.PROT_READ | mmap.PROT_EXEC)
mm.write(shell_code)

Finally we obtain the address of the memory and create a C Function Pointer using ctypes and this address:

import ctypes
restype = ctypes.c_int64
argtypes = tuple()
ctypes_buffer = ctypes.c_int.from_buffer(mm)
function = ctypes.CFUNCTYPE(restype, *argtypes)(ctypes.addressof(ctypes_buffer))
function()

The function create_function_from_shellcode in Simple.py implements this. As I stated above this code was meant as a learn-project. I recommend using directly the code I described above (or Simple.py which basically contains this code) instead of PyShellCode itself, because the dependency on the shared-library of the C-Implementation is unnecessary for you.

The C-Implementation looks similar.

Performance

In my tests PyShellCode yields the expected performance e.g.: PyShellCode is 100 times faster than pure python and 10 times faster than numba for equivalent implementations for finding the longest period of the collatz-sequence below a threshold.

python3 examples/collatz.py

Python [6.17894043999695, 6.065295364998747, 6.235952811999596]

Numba [0.603332129001501, 0.6196587439990253, 0.6055442029974074]

PyShellCode [0.06954922499426175, 0.06870782400073949, 0.06622266400518129]

Resources

Other work