Computer Systems Organization

CSCI-UA.0201(003), Spring 2026

Lab-3: Binary Mystery

For this lab, you need to use a native x86 Linux machine instead of inside a docker container. First, you follow instructions below to log into such a Linux server maintained by CIMS. Next, you clone the lab's repository on this CIMS Linux server. Finally, you can follow the instructions to do the lab on the CIMS Linux server.

Log into a CIMS Linux server from your laptop

You will do the lab on a CIMS server called snappy2.cims.nyu.edu, which you access via a gateway host called access.cims.nyu.edu. To do so, open a terminal on your laptop (if you use Windows, type wsl in PowerShell).

$ ssh @access.cims.nyu.edu
[Enter your password. Then select 1 or 2 for further authentication]

Continue to log into snappy1 by typing

[netid@access2 ~]$ ssh snappy2

Cloing the lab repository

First, click on Lab3's github classroom invitation link (posted on Campuswire) and select your NYU netid. Next, clone your repo by typing the following

snappy2$ mkdir -p cso-labs
snappy2$ cd cso-labs
snappy2$ git clone git@github.com:nyu-cso-sp26/lab3-<YourGithubUsername>.git  lab3

Do lab3 by uncovering the mystery of x86 assembly

The lab contains two parts. In part-1, you reconstruct semantically equivalent C functions based on their assembly and execution. In part-2, you perform a control flow hijacking attack by exploiting a buffer overflow vulnerability in the given program.

Part-1: Reconstruct equivalent C code from assembly

In this part, you are to reconstruct five C functions by examining their corresponding machine code and running the executable in gdb.

The five C functions you are to reconstruct are named ex[1-5](...). We have compiled their C implementation into object files ex[1-5]_sol.o and withheld the source code. We have also implemented a tester that tests these five functions using various inputs. The tester's binary tester_sol is also given to you. If you make and run tester_sol, you should see that it passes all tests, as expected.

Figure out what each function ex[1-5> does according to hints, and write the corresponding C code in ex[1-5].c.

No goto's
For this lab, the only files that you should modify are ex[1-5].c. Furthermore, your implementation should not contain any goto statements. .
Test your solution

After you've finished each function (remember to remove the assert(0) statement), you compile and create a new tester that links to your implementation ex[1-5].o instead of ex[1-5]_sol.o. You can check whether your reconstructed functions are correct or not by running this new tester:

$ make
$ ./tester
Testing ex1...
ex1 passed
Testing ex2...
ex2 passed
Testing ex3...
ex3 passed 
Testing ex4...
ex4 passed
Testing ex5...
ex5 passed
The above ouput ocurrs when all your ex{1-5} functions pass the test.

To test multiple times, run ./tester -r with the -r option. This runs the tester using a new seed for its random number generator.

Some of you might want to skip around and implement the five ex* functions in arbitary order. This is a good strategy if you are stuck on some function. To test just ex2, type ./tester -t 2. Ditto with other functions.

Note: Passing the test does not guarantee that your implementation is not necessarily correct. During grading, we may manually examine your source code to determine its correctness.

Part-1 Hints

Suppose you set out to figure out what function ex1 (implemented in ex1_sol.o) does. There are two approaches to do this. You should use them both to help uncover the mystery.

Do not try to match assembly

It is not the right approach to try to match the object code of your C function line-by-line to those contained in ex{1-5}-sol.o. Doing so is painful and not necessary. Differences in the compiler versions, compilation flags, and small differences in C code will all result in different object code, although they do not affect the code's semantics. Therefore, trying to find a C function that generates the same object code is likely futile.

Explanations on some unfamiliar assembly and others

For this lab, you need to review the lecture notes and textbook to refresh your understanding of x86 assembly. Below are some additional information not covered in the lecture notes that are helpful for this lab as well.

For those of you who want to go out in the world to explore other object files, you will find the official Intel instruction set manual useful. Note that in the Intel manual, the source and destination operands are reversed in an instruction (i.e. destination operand first, source operand last). In the lecture notes and gdb/objdump's disassembled output, the destination operand appears last in an instruction. These differences are due to two assembly syntaxes, AT&T syntax and Intel syntax. The GNU software (gcc, gdb etc) and lecture notes use AT&T syntax which puts the destination operand last and Intel manual (of course) uses Intel syntax which puts the destination operand first.

Part-2: Buffer Overflow Hack

You are given a program overflow whose source code has been withheld. This program takes a single command line input and prints some output. Your job is to craft a malicious input such that the program will end up invoking a function called success that it is never supposed to invoke under its normal control flow.
void success()
{
  printf("you successfully hijacked overflow's control flow\n");
  exit(0);
}
Put your malicious argument in a file called bad_arg and run program overflow. If you are successful, you should see the following:
snappy2$ cat bad_arg |xargs ./overflow
you successfully hijacked overflow's control flow

In the above command, cat bad_arg prints the content of file bad_arg to stdout which is connected via a pipe | to the stdin of command xargs ./overflow. xargs reads stuff from stdin and uses them as arguments for command overflow.

Handin Procedure

Follow these instructions.