Originally I was going complete a project to develop a Python script (or two) that aggregates performance data from various parts of a Linux box and somehow present it in a GUI/dashboard. The catch was it had to be usable over SSH and without an X server, and I already found a potential solution to this when running into the alsamixer utility in Arch Linux:

alsamixer-ui

With a little digging I found alsamixer uses something called the curses/ncurses API, which was originally created for BSD UNIX and ported to other systems. Designed for compatibility across terminals without an X server, a curses interface should display regardless of hardware type or graphics driver. It also enables the output of multiple commands to be positioned on the same screen. This was exactly what I wanted for the ‘dashboard’.

Unfortunately there was a ‘change of plan’ (some people are ruthless in their quest for brownie points), and I decided to test this on something more basic instead: data wiping and recovery.

Using (n)curses
If I knew about curses API last year it would have saved me the trouble of creating stuff in the PythonCard designer, maintaining multiple resource files and rewriting my software as window object handlers. My software would have been more portable, not requiring users to install additional libraries, and the entire thing could have been done in a single (although considerably long) Python script.

The curses API is provided as a core Python module:

from os import system
import curses
myscreen = curses.initscr()

myscreen.border(0)
myscreen.addstr(5, 5, "HELLO WORLD")
input = myscreen.getstr(12, 20, 50)
system("cat /proc/iomem")
myscreen.refresh()
myscreen.getch()

curses.endwin()

And it has simplicity – After initialising curses the UI here is declared as the myscreen namespace, and this namespace includes the border(), addstr(), getstr(), refresh(), getch() and endwin() functions. Likely a few others also.
Variables passed to myscreen.addstr() can be declared earlier in the program, so if it’s dealing with multiple output data, they can be displayed at predefined locations in the UI. e.g.
HelloTop = 12
HelloLeft = 20
myscreen.addstr(HelloTop, HelloLeft, "HELLO WORLD")

Note: It really helps to comment the code from this point.

Capturing Events
Almost everyone who’s played with coding learns how to create a simple menu that prints a list of options and reads the user’s choice as an input string. While there’s no problem doing this in a curses interface, a UI that responds to a single keystroke feels more professional.

option = appwin.getch()
if option == ord('1'):
appwin.addstr(15, 15, "Volume erasure selected.", curses.color_pair(1))
if option == ord('2'):
appwin.addstr(15, 15, "Acquisition selected.", curses.color_pair(1))

And this is what I have at the moment:

xerodrive-ui-1

The System
Essentially this program will be an interface for the dd and foremost utilities. I might replace these with my own handlers later on, but for the moment the program must pass variables and options to external binaries. Notice that I included the line in my earlier example:
system("cat /proc/iomem")

That’s the crude and basic way of doing it. It’s possible (and probably better from a security perspective) to have the program run a shell command as a sub-process. e.g.

import subprocess
processes = subprocess.call(['df', '-h'])

This means the Python script will have ownership of the running shell command. When the script terminates, any processes it starts would also be removed. The subprocess namespace might also solve the problem of how to pipe command line output back into the curses UI, but I’ll get to that whenever.

Erasing a Volume
Adding code for wiping the drive was perhaps the trickiest problem to solve, even though I just wanted something like:

read TargetVolume
dd if =/dev/urandom of=TargetVolume

Try as I might, it would only create a file called ‘TargetVolume’ in the project directory. Part of the solution was found in a paper titled ‘High-Speed Data Shredding using Python‘, and it was by chance that I discovered the appwin.getstr() input had to be UTF-8 encoded:

appwin.addstr(2, 1, "Volume to erase: ", curses.color_pair(2))
Volume = appwin.getstr(2, 20).decode(encoding="utf-8")
appwin.addstr(4, 1, "Erasing volume. This might take a while...")
subprocess.call("dd if=/dev/urandom of=" + Volume + " bs=512k", shell=True)

So that part works now. The rest should be a matter of applying variations of this, and tweaking the software until it reliably erases filesystems. Currently it erases data, just not enough to prevent recovery by file carving.

xerodrive-ui-2

By the way, it’s worth reading Gerald’s comments on last month’s post. This might be enough for an SSD, but don’t take anyone’s word for it. Give it a try.

Advertisements