CS 161 Notes Lec 1-2

Overview

Lecture 1: Introduction and Security Principles
  1. Introductions

  2. Course logistics

  3. What is security? Why is it important?

  4. Security principles

Lecture 2: x86 Assembly and Call Stack
  1. How do computers represent numbers as bits and bytes?

  2. How do computers interpret and run the programs we write?

  3. How do computers organize segments of memory?

  4. How do x86 assembly work?

  5. How to call a function in x86?

What is security? Why is it important?

Security is enforcing a desired property in the presence of an attacker

  • It’s not privacy(protecting data from unauthorized access).

  • It’s not safety(enforcing a desired property, but in the presence of random nature).

Why is security important?

  • … You know why :)

Security principles

Take a look at the textbook, https://textbook.cs161.org/principles/principles.html

Principles listed in textbook includes:

  • Know you threat model

  • Consider human factors

  • Security is economics

  • Detect if you can’t prevent

  • Defense in depth

  • Least privilege
    • In technical terms, give a program the set of access privileges that it legitimately needs to do its job—but nothing more. Try to minimize how much privilege you give each program and system component.
  • Separation of responsibility

  • Ensure complete mediation

  • Shannon’s Maxim
    • “Security through obscurity” refers to systems that rely on the secrecy of their design, algorithms, or source code to be secure. The issue with this, however, is that it is extremely brittle and it is often difficult to keep the design of a system secret from a sufficiently motivated attacker.

    • Shannon’s Maxim states that the attacker knows the system that they are attacking.

  • Use fail-safe defaults

  • Design security from the start

  • The trusted computing base(TCB)
    • TCB design principles:
      • Unbypassable (or completeness): There must be no way to breach system security by bypassing the TCB.

      • Tamper-resistant (or security): The TCB should be protected from tampering by anyone else. For instance, other parts of the system outside the TCB should not be able to modify the TCB’s code or state. The integrity of the TCB must be maintained.

      • Verifiable (or correctness): It should be possible to verify the correctness of the TCB. This usually means that the TCB should be as simple as possible, as generally it is beyond the state of the art to verify the correctness of subsystems with any appreciable degree of complexity.

  • TOCTTOU Vulnerabilities(the time to check the time of use)

How do computers represent numbers as bits and bytes?

Since I’m already quite familiar with the relevant knowledge, I’m not gonna explain them in detail but just copy them from the lecture slides/textbook.
In computers, all data is represented as bits, 4 bits = 1 nibble, 8 bits = 1 byte

… No more interesting things in this part :)

How do computers interpret and run the programs we write?

  • Compiler: Converts C code into assembly code(x86, RISC-V)

  • Assembler: Converts assembly code into machine code(raw bits)

  • Linker: Deals with dependencies and libraries

  • Loader: Sets up memory space and runs the machine code

    • At runtime, the loader tells your OS to give your program a big blob of memory

How do computers organize segments of memory?

On a 32-bit system, the memory has 32-bit addresses(this course also consider systems as 32-bit according to the lecture slides)

Each memory address refers to one byte(it’s actually called byte-addressable)

x86 memory layout:

Note that in this course higher memory addresses are usually drawn at the top of graphs, personally I’m more used to the opposite way.

Stack layout:

  • In unoptimized code, local variables are always allocated on the stack

  • Individual variables within a stack frame are stored with the first variable at the highest address

  • Members of a struct are stored with the first variable at the lowest address

  • Global variables are stored with the first variable at the lowest address

An example of stack layout:

struct foo {
  long long f1; // 8 bytes
  int f2; // 4 bytes
  int f3; // 4 bytes
}

void func(void){
  int a; // 4 bytes
  struct foo b;
  int c;  4 bytes
}

The stack of the above code snippet would be

How do x86 assembly work?

A brief introduction of x86:

List of facts about x86:

  • Little-endian
    • The least-significant byte of multi-byte numbers is placed at the first/lowest memory address
  • Operations can use data in registers or in memory
    • RISC-V can ‘t use data in memory directly
  • Variable-length instructions

x86 registers:

  • Storage units as part of the CPU architecture(not part of the memory)

  • Only 8 main general-purpose registers:
    • eax, ebx, ecx, edx, esi, edi: general-purpose

    • esp: Stack pointer

    • ebp: Base pointer

  • Instruction pointer register: eip

  • Note: x86-64 uses the r-prefix instead of the e-prefix for 64-bit registers

  • Note: there’s also a register called eflags

AT&T syntax:

  • The course uses AT&T syntax, since it’s used by GDB

  • The alternative is Intel syntax

  • Registers are preceded with a percent sign %

  • Immediates are preceded with a dollar sign $
    • example: $1, $161
  • Memory references uses parentheses and can have immediate offsets
    • example: 8(%esp)
  • Many other rules can be found: https://en.wikibooks.org/wiki/X86_Assembly

How to call a function in x86?

See here for more information:

https://wiki.osdev.org/Calling_Conventions

https://textbook.cs161.org/memory-safety/x86.html

An understood way for functions to call other functions and know what state the processor will return in

Call convention is critical for security, but I’m not gonna go to details here :)

Written on May 10, 2023
-->