http://www.cotyrmiller.com > rants > Combining 2 powers, Python & Assembly (MacOS)


Combining 2 powers, Python & Assembly on MacOS (X)


There are two insanely powerful languages each one for a completely different reason than the other. On one hand you have 

python, a language witch is interpreted making it slow and is so high level that the amount of abstraction means you have to access 

to hardware. However, this provides the benefit of being able to wright vast amounts of code painlessly and quickly. Because of this, 

python is often my first choice for doing anything, however sometimes its abstraction can get in the way.


On the other end of the stick, you have assembly code, Assembly code is extremely powerful, it can preform any operation 

that a computer is able to preform as you are basically writing machine code it’s self. It also has the ability to run very fast, very 

efficient and very compact as there is no middle man software, it runs straight on the hardware itself. The problem with assembly 

code (Specifically when it comes to modern x86 or x86_64 architecture) is that the code tends to be fairly complicated and specially 

on macOS it is very difficult to find good 64bit documentation… witch is a big problem as apple is rumored to be phasing out 32bit 

support in the near future.


So, let’s build something that can play to both languages strengths, why choose one when we could use both at the 

same time!?


So for today I decided the way I was going to go about this is very simple. We’ll start in python, and call an assembler 

application and pass some arguments to it, or least a single byte string, modify the data in the assembly application, that we’ll just 

add one to it then we’ll capture the output (in python) from the assembler application.


Let’s start with writing the assembler code. So since we’ll be collecting arguments as our input in the assembler code then we need 

to understand how MacOS/Linux passes console arguments.


This is pretty straight forward, whenever you launch a program from the terminal, the system takes all the the arguments the file name

of the application and puts each one in it’s own string accessible to your application. It then pushes the address of those strings and

the total argument or string count onto the stack. Also note that the file name is the first argument, so you will always have at least

1 argument.


       RSP = 64bit stack pointer

    RSP [   +0   |   +8   |  +16   ]

                /            |           \

   Number of.        |       Address of first 

   Arguments        |        argument string.

                            |

           Address of file name string


So the first thing we will do is check to see if we have 2 arguments, if we have more or less we will just ignore it and close the 

application. While as not something you would not want to do (you’d probably want to provide an error on exit) it will be fine for the 

purpose of this demo.


start:

        ; check if we have the correct number of arguments

        mov     rax, [rsp+0]

        cmp     rax, 2  

        je      .contine

        ; if not exit.

        .exit:

                mov     rax, ints.exit

                mov     rdi, 0

                syscall


Next if our code has made it that far, we’ll grab the single byte from our string add one to it and store it in a more local buffer 

so it’s less of a pain to deal with.


.contine:

        ; grab byte from string.

        mov     rsi, [rsp+16]

        lodsb

        ; add 1 to it and store it in our buffer.

        inc     al

        mov     rdi, place

        stosb


Then we’ll follow it up by printing our modified data to the terminal so that python can capture it. 


        mov     rsi, place

        mov     rdx, 1 


        mov     rax, ints.write   ; rax = write command.

        mov     rdi, 1            ; stdout

        syscall                   ; write it to terminal.

        jmp     .exit             ; go to exit.


(For full source code see below). 


Alright now that we have our assembler code we can assemble it and we can run it as


Macintosh:pythonasm cotyrmiller$ ./bitflip e

f


And just like that, we have taken in the letter “e” as an argument, and our assembly code has dumped out the letter “f”! Just 

note that this is raw, if you send “9” you will get “:” as it is the next char available as we are changing the literal numerical value of 

the data. So now that we’ve completed the ASM code it’s time to work on our python code. Fortunately most of the real work is done.


Let’s just create our header, we’ll need to ensure that the code runs as python (MacOS will attempt to run it as a bash script by 

default) this is super simple as we just need to point at the python interpreter in a comment at the top of the file and we will need to 

import the subproccess library.


#!/usr/bin/env python

import subprocess


The subprocces library is what we’ll use to execute and retrieve output from our assembly application.


stringToAdd = "8"

a = subprocess.check_output(["./bitflip", stringToAdd]) # open the asm application as “./bitflip 8” and capture it’s output in ‘a’

print a    # Print the captured output.


And… that’s it… That’s our python code. A whole whopping 5 lines… Running this will return and print 

a “9”. And here is the total build and run process of all of our work.


Macintosh:pythonasm cotyrmiller$ ./build

Macintosh:pythonasm cotyrmiller$ ./pigo.pi

9

Macintosh:pythonasm cotyrmiller$ 


THAT’S IT! We are done! We now have now successfully combined assembly code and python code 

together… Two opposites… Two giants… married together into a powerful ball of… something… You can 

download the full work environment from code to build scripts bellow.


Happy coding - Coty R Miller



Download compressed ZIP of full source code: rantspythonasm.zip