Projects#
C projects in the operator’s sweet spot, small system utilities, parsers, kernel-adjacent code, and the foundations the operator will eventually have to read in a CVE write-up.
Protocol parser#
A safe parser for a binary network protocol. Write one when reverse-engineering a private wire format or building a high-throughput frontend (the parser has to be in C for performance).
/* op_parser.c */
#include <stdint.h>
#include <stddef.h>
#include <string.h>
typedef struct {
uint8_t version;
uint16_t length;
uint16_t flags;
const uint8_t *payload;
} op_packet_t;
/* Returns 0 on success, -1 on malformed input. */
int op_parse(const uint8_t *buf, size_t buf_len, op_packet_t *out) {
if (buf_len < 5) return -1; /* fixed header is 5 bytes */
out->version = buf[0];
memcpy(&out->length, buf + 1, 2);
memcpy(&out->flags, buf + 3, 2);
if (out->length > buf_len - 5) return -1;
out->payload = buf + 5;
return 0;
}
Build with sanitisers enabled while developing.
$ clang -std=c11 -Wall -Wextra -Werror \
-fsanitize=address,undefined -g -O1 \
op_parser.c test_parser.c -o test_parser
$ ./test_parser
syscall wrapper#
A small library that wraps a syscall you want to use
from higher-level code (Python via ctypes, Go via cgo).
/* memfd.c */
#define _GNU_SOURCE
#include <sys/mman.h>
#include <stdint.h>
#include <unistd.h>
int op_memfd_create(const char *name, unsigned int flags) {
return memfd_create(name, flags);
}
ssize_t op_write_all(int fd, const void *buf, size_t n) {
const uint8_t *p = buf;
while (n > 0) {
ssize_t w = write(fd, p, n);
if (w < 0) return -1;
p += w; n -= (size_t)w;
}
return 0;
}
Compile as a shared library for FFI from Python or Go.
$ clang -fPIC -shared -o libmemfd.so memfd.c
File scanner#
The classic operator tool, walk a tree, hash each file, write a JSON-Lines manifest. Fast because it’s in C.
#include <stdio.h>
#include <stdlib.h>
#include <ftw.h>
#include <openssl/sha.h>
static int handle(const char *path, const struct stat *st,
int type, struct FTW *ftw) {
if (type != FTW_F) return 0;
FILE *f = fopen(path, "rb");
if (!f) return 0;
SHA256_CTX c; SHA256_Init(&c);
unsigned char buf[65536]; size_t n;
while ((n = fread(buf, 1, sizeof(buf), f)) > 0)
SHA256_Update(&c, buf, n);
fclose(f);
unsigned char d[SHA256_DIGEST_LENGTH];
SHA256_Final(d, &c);
printf("{\"path\":\"%s\",\"sha256\":\"", path);
for (int i = 0; i < SHA256_DIGEST_LENGTH; i++)
printf("%02x", d[i]);
printf("\",\"size\":%lld}\n", (long long)st->st_size);
return 0;
}
int main(int argc, char **argv) {
if (argc < 2) return 2;
nftw(argv[1], handle, 16, FTW_PHYS);
return 0;
}
Embedded firmware#
The operator’s “small thing that runs forever” project. C is
inevitable on bare-metal microcontrollers (STM32, AVR, ESP32 in C
mode). Build with arm-none-eabi-gcc; debug via OpenOCD +
GDB.
/* main.c (STM32) */
#include "stm32f4xx.h"
void main(void) {
RCC->AHB1ENR |= RCC_AHB1ENR_GPIODEN;
GPIOD->MODER |= (1 << (12 * 2));
while (1) {
GPIOD->ODR ^= (1 << 12);
for (volatile int i = 0; i < 1000000; i++);
}
}
References#
Setup for the toolchain (compiler, build system, debugger).
Libraries for stdlib, POSIX, and operator-relevant third-party (
libcurl,libssh,openssl).Kernel for the kernel context most C projects target.
Linux Programming Interface, Michael Kerrisk (the standard reference for Linux system programming in C).