Proctal

Documentation

Overview

With this API your program can access memory, execute code, pause execution and watch for memory accesses in other programs.

What it looks like

The API is available as a C library. You need to link your program with libproctal.so and include proctal.h in your code.
The C library contains functions that operate on a handle that you create.

proctal_t proctal = proctal_open();

proctal_pid_set(proctal, 12345);

int output = 42;

void *address = proctal_allocate(proctal, sizeof(output));

proctal_write(proctal, address, &output, sizeof(output));

int input;

proctal_read(proctal, address, &input, sizeof(input));

proctal_deallocate(proctal, address);

You may create and use more than one handle to access different programs, provided that you do not use the same handle in multiple threads.
Errors are reported through a function dedicated to returning error codes.

int code = proctal_error(proctal);

You need to destroy the handle when you're done with it.

proctal_close(proctal);

Why use this

While the command line interface is designed as a generic tool for humans, the C library is meant to be as efficient as possible for the machine.
If you were to create a script that runs the search command several times, passing the output of a previous run to the next one, the tool would have to convert the bits in memory to readable text characters and then back to bits when they're piped to the next command which results in a lot of computing power being wasted in just converting data back and forth. For a one off program this performance bottleneck can be ignored but if you are creating your own specialized tool or extending an existing program, it makes more sense to use the library.

Example

This is a tool built with the library that can make a program print Hello, world!.

#include <stdlib.h>
#include <stdint.h>
#include <stdio.h>

#include <proctal.h>

int main (int argc, char **argv)
{
	if (argc != 2) {
		fprintf(stderr, "Usage: %s PID\n", argv[0]);
		return 1;
	}

	int pid = atoi(argv[1]);

	if (pid == 0) {
		fprintf(stderr, "Given PID is not valid\n");
		return 1;
	}

	const char output[] = "Hello, world!\n";
	char code[] = {
		// mov rsi, <address>
		0x48, 0xbe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
		// mov rax, 1
		0x48, 0xc7, 0xc0, 0x01, 0x00, 0x00, 0x00,
		// mov rdi, 1
		0x48, 0xc7, 0xc7, 0x01, 0x00, 0x00, 0x00,
		// mov rdx, 14
		0x48, 0xc7, 0xc2, 0x0e, 0x00, 0x00, 0x00,
		// syscall
		0x0f, 0x05
	};

	proctal_t proctal = proctal_open();

	if (proctal_error(proctal)) {
		fprintf(stderr, "Failed to open Proctal.\n");
		proctal_close(proctal);
		return EXIT_FAILURE;
	}

	proctal_pid_set(proctal, pid);

	void *allocated_memory = proctal_allocate(proctal, sizeof output);

	if (proctal_error(proctal)) {
		fprintf(stderr, "Failed to allocate memory in process %d.\n", proctal_pid(proctal));
		proctal_close(proctal);
		return EXIT_FAILURE;
	}

	proctal_write(proctal, allocated_memory, output, sizeof output);

	if (proctal_error(proctal)) {
		fprintf(stderr, "Failed to write to memory in process %d.\n", proctal_pid(proctal));
		proctal_deallocate(proctal, allocated_memory);
		proctal_close(proctal);
		return EXIT_FAILURE;
	}

	code[2] = (char) ((uintptr_t) allocated_memory >> 8 * 0 & 0xFF);
	code[3] = (char) ((uintptr_t) allocated_memory >> 8 * 1 & 0xFF);
	code[4] = (char) ((uintptr_t) allocated_memory >> 8 * 2 & 0xFF);
	code[5] = (char) ((uintptr_t) allocated_memory >> 8 * 3 & 0xFF);
	code[6] = (char) ((uintptr_t) allocated_memory >> 8 * 4 & 0xFF);
	code[7] = (char) ((uintptr_t) allocated_memory >> 8 * 5 & 0xFF);
	code[8] = (char) ((uintptr_t) allocated_memory >> 8 * 6 & 0xFF);
	code[9] = (char) ((uintptr_t) allocated_memory >> 8 * 7 & 0xFF);

	proctal_execute(proctal, code, sizeof code);

	if (proctal_error(proctal)) {
		fprintf(stderr, "Failed to execute code in process %d.\n", proctal_pid(proctal));
		proctal_deallocate(proctal, allocated_memory);
		proctal_close(proctal);
		return EXIT_FAILURE;
	}

	proctal_deallocate(proctal, allocated_memory);
	proctal_close(proctal);
	return EXIT_SUCCESS;
}