Classroom Public page

RE-011 Lab Artifacts: Manifest and Self-Paced Fallback

640 words

This directory documents every artifact referenced across RE-011 labs, with a self-paced fallback path for students who are not in an instructor-led cohort.


Artifact manifest

Lab Artifact name Description Cohort delivery
Lab 1 lab1-files.tar.gz 10 files of mixed formats, no extensions Instructor-provided tar archive
Lab 4 lab4_target Stripped x86-64 ELF with functions to navigate Instructor-provided binary
Lab 5 lab5_target Stripped x86-64 ELF: the binary from which the listing was extracted Instructor-provided binary
Lab 7 lab7_target Stripped x86-64 ELF password checker (obfuscated) Instructor-provided binary
Lab 8 lab8_target Stripped x86-64 ELF with conditional bypass target Instructor-provided binary

Labs 2, 3, 6, 9 use binaries already on the student's system (/usr/bin/ls, /bin/bash, etc.) and do not require instructor-provided artifacts.


Self-paced fallback: Lab 1

Lab 1 asks you to identify 10 files by format using only file, xxd, and strings. If you are self-paced, build your own 10-file set:

mkdir lab1 && cd lab1

# 1. x86-64 dynamically linked ELF executable
cp /usr/bin/ls        file-01

# 2. x86-64 ELF executable (different tool, different symbol set)
cp /usr/bin/file      file-02

# 3. x86-64 shared library (ET_DYN, not an executable)
cp /usr/lib/x86_64-linux-gnu/libc.so.6  file-03

# 4. ELF relocatable object (.o file): compile one from scratch
echo 'int add(int a, int b){ return a+b; }' > /tmp/add.c
gcc -c /tmp/add.c -o file-04

# 5. Statically linked x86-64 ELF (no dynamic section, no .interp)
sudo apt-get install -y busybox-static
cp /bin/busybox-static  file-05

# 6. 32-bit ARM ELF (ET_EXEC, different e_machine than x86-64)
sudo apt-get install -y gcc-arm-linux-gnueabi
printf '#include<stdio.h>\nint main(){puts("hello");return 0;}' > /tmp/hello.c
arm-linux-gnueabi-gcc /tmp/hello.c -o file-06 -static

# 7. MIPS32 ELF (demonstrates e_machine value for MIPS)
sudo apt-get install -y gcc-mips-linux-gnu
mips-linux-gnu-gcc /tmp/hello.c -o file-07 -static

# 8. ZIP archive (magic bytes PK\x03\x04; no ELF magic)
zip file-08.zip /etc/hostname && mv file-08.zip file-08

# 9. PNG image (magic bytes \x89PNG; binary data with no ELF)
cp /usr/share/pixmaps/debian-logo.png file-09 2>/dev/null || \
  python3 -c "
import struct, zlib
def png(w,h):
    def chunk(t,d): crc=zlib.crc32(t+d)&0xffffffff; return struct.pack('>I',len(d))+t+d+struct.pack('>I',crc)
    sig=b'\x89PNG\r\n\x1a\n'
    ihdr=chunk(b'IHDR',struct.pack('>IIBBBBB',w,h,8,2,0,0,0))
    row=b'\x00'+b'\xff\x00\x00'*w
    idat=chunk(b'IDAT',zlib.compress(row*h))
    iend=chunk(b'IEND',b'')
    return sig+ihdr+idat+iend
open('file-09','wb').write(png(8,8))"

# 10. Script with no extension (text, not binary -- contrasts with the ELF files)
printf '#!/bin/bash\necho "hello world"\n' > file-10
chmod +x file-10

You now have 10 files that span: dynamically linked ELF, shared library, relocatable object, static ELF, two cross-arch ELF binaries (ARM and MIPS), a ZIP, a PNG, and a shell script. This gives you the full range of format identifications the lab requires.

Rename them in random order if you want to replicate the "mystery file" experience of the cohort version.


Self-paced fallback: Lab 4 (lab4_target)

Lab 4 is Ghidra navigation practice. You need a stripped x86-64 ELF with several distinct functions. Compile the source below, then strip it:

/* lab4_source.c */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

static int compute_checksum(const char *s) {
    int total = 0;
    while (*s) total += (unsigned char)*s++;
    return total;
}

static void report(const char *label, int val) {
    printf("%-20s %d\n", label, val);
}

static int validate(const char *input) {
    int cs = compute_checksum(input);
    return (cs == 0x1a4);
}

int main(int argc, char *argv[]) {
    if (argc < 2) {
        fprintf(stderr, "usage: %s <input>\n", argv[0]);
        return 1;
    }
    int cs = compute_checksum(argv[1]);
    report("checksum", cs);
    if (validate(argv[1])) {
        puts("Access granted");
        return 0;
    }
    puts("Access denied");
    return 1;
}
gcc -O1 lab4_source.c -o lab4_target
strip lab4_target

This gives you a stripped binary with four named functions in the source that become FUN_ labels in Ghidra. compute_checksum and validate will often be inlined at -O1 on some builds, which is pedagogically interesting -- you may see fewer functions in Ghidra than in the source. Use -O0 if you want to guarantee all four are visible as separate functions.


Self-paced fallback: Lab 5 (lab5_target)

Lab 5 gives you the disassembly listing directly and asks you to reconstruct C from it. You do not need the binary for Parts A-C. Part D asks you to cross-check against Ghidra's decompiler.

For Part D, compile a binary that has similar structure (a linked-list traversal with null check, loop, and fprintf calls). The exact bytes will differ from the listing, but the decompiler output will exercise the same skills:

/* lab5_source.c */
#include <stdio.h>
#include <stdlib.h>

typedef struct Node {
    unsigned long data;
    struct Node *next;
    FILE *out;
} Node;

typedef struct List {
    Node *head;
} List;

void process_list(List *lst) {
    if (!lst || !lst->head) {
        fprintf(stderr, "Error: null list\n");
        return;
    }
    fprintf(stdout, "Processing list:\n");
    Node *cur = lst->head;
    do {
        Node *nxt = cur->next;
        fprintf(cur->out, "  [%lu]\n", cur->data);
        cur = nxt;
    } while (cur);
}

int main(void) {
    FILE *f = stdout;
    Node n2 = {42, NULL, f};
    Node n1 = {7,  &n2,  f};
    List lst = {&n1};
    process_list(&lst);
    process_list(NULL);
    return 0;
}
gcc -O2 lab5_source.c -o lab5_target
strip lab5_target

Load lab5_target in Ghidra, navigate to process_list (search symbols for process_list, or scan FUN_ labels for a function that calls fprintf twice), and use the decompiler output as your Part D cross-check.


Self-paced fallback: Lab 7 (lab7_target)

Lab 7 is the dynamic-vs-static methodology lab. You need a password checker that accepts one argument and prints "Access granted" or "Access denied". The binary has obfuscation that makes ltrace insufficient on its own.

Compile with the transformation below and strip it. The ROT13 obfuscation is simple enough that static analysis can recover it, but it defeats a naive ltrace scan:

/* lab7_source.c */
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

/* ROT13 transform -- simple obfuscation: key is stored transformed */
static void rot13(char *s) {
    for (; *s; s++) {
        if (*s >= 'a' && *s <= 'z') *s = 'a' + (*s - 'a' + 13) % 26;
        else if (*s >= 'A' && *s <= 'Z') *s = 'A' + (*s - 'A' + 13) % 26;
    }
}

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

    /* Key stored in its ROT13 form; transform the input before comparing */
    char stored[] = "ercbfvgbel";   /* ROT13 of "repository" */
    char input[64];
    strncpy(input, argv[1], sizeof(input) - 1);
    input[sizeof(input) - 1] = '\0';
    rot13(input);

    if (strcmp(input, stored) == 0) {
        puts("Access granted");
        return 0;
    }
    puts("Access denied");
    return 1;
}
gcc -O1 lab7_source.c -o lab7_target
strip lab7_target

Note for students: ltrace ./lab7_target test will show a strcmp call, but the arguments will both be ROT13-transformed strings -- ltrace reveals the transformed input and the stored key, not the plaintext key. Finding which obfuscation is in play and recovering the plaintext key requires static analysis. This is the "which one is obfuscated" question Lab 7 asks you to answer.


Self-paced fallback: Lab 8 (lab8_target)

Lab 8 is binary patching. You need a binary with a conditional check you can bypass with a single-byte patch. The source below has a license-flag check and an optional ptrace anti-debug routine:

/* lab8_source.c */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ptrace.h>

static int is_debugged(void) {
    return ptrace(PTRACE_TRACEME, 0, NULL, NULL) == -1;
}

static int check_license(const char *key) {
    /* Simple checksum: must sum to 0x1f4 (500) */
    int total = 0;
    for (const char *p = key; *p; p++) total += (unsigned char)*p;
    return total == 0x1f4;
}

int main(int argc, char *argv[]) {
    if (argc != 2) {
        fprintf(stderr, "usage: %s <key>\n", argv[0]);
        return 1;
    }
    if (is_debugged()) {
        puts("Debugger detected");
        return 1;
    }
    if (check_license(argv[1])) {
        puts("Correct");
        return 0;
    }
    puts("Wrong");
    return 1;
}
gcc -O1 lab8_source.c -o lab8_target
strip lab8_target

The lab asks you to:

  1. Locate the conditional jump that leads to "Wrong"
  2. Compute the file offset using readelf -S and the virtual-address-to-file-offset formula
  3. Patch the jump instruction (typically je -> jne = change 0x74 to 0x75, or jne -> je)
  4. Verify the patched binary prints "Correct" regardless of input

The is_debugged function is the anti-debug element. Lab 8 asks you to identify it but does not require you to bypass it (the ptrace check fails differently in different environments).


Artifact manifest current as of RE-011 v0.1. Self-paced C sources are pedagogically equivalent substitutes; they are not byte-for-byte matches to the cohort binaries.