Machine-Level Programming V - Advanced Topics

Class: CSCE-312


Notes:

Today

Memory Layout

x86-64 Linux Memory Layout

Pasted image 20251023125029.png|300

Memory Allocation Example

char big_array[1L<<24];  /* 16 MB */
char huge_array[1L<<31]; /*  2 GB */

int global = 0;

int useless() { return 0; }

int main ()
{
    void *p1, *p2, *p3, *p4;
    int local = 0;
    p1 = malloc(1L << 28); /* 256 MB */
    p2 = malloc(1L << 8);  /* 256  B */
    p3 = malloc(1L << 32); /*   4 GB */
    p4 = malloc(1L << 8);  /* 256  B */
 /* Some print statements ... */
}

Where does everything go?

Pasted image 20251023131056.png|75

x86-64 Example Addresses

_address range ~2^47_
Pasted image 20251023133812.png|500

Buffer Overflow

Recall: Memory Referencing Bug Example

typedef struct {
  int a[2];
  double d;
} struct_t;

double fun(int i) {
  volatile struct_t s;
  s.d = 3.14;
  s.a[i] = 1073741824; /* Possibly out of bounds */
  return s.d;
}
fun(0)  ➙	3.14
fun(1)  ➙	3.14
fun(2)  ➙	3.1399998664856
fun(3)  ➙	2.00000061035156
fun(4)  ➙	3.14
fun(6)  ➙	Segmentation fault

Pasted image 20251023135202.png|500

Such problems are a BIG deal

String Library Code

Implementation of Unix function gets()

/* Get string from stdin */
char *gets(char *dest)
{
    
	int c = getchar();
    char *p = dest;
    while (c != EOF && c != '\n') {
        *p++ = c;
        c = getchar();
    }
    *p = '\0';
    return dest;
}

Similar problems with other library functions

Vulnerable Buffer Code

/* Echo Line */  
void echo()  
{  
    char buf[4];  /* Way too small! */  
    gets(buf);  
    puts(buf);  
}
void call_echo() {
    echo();
}

Pasted image 20251023135950.png|350

Buffer Overflow Disassembly

Pasted image 20251023161809.png|550

Buffer Overflow Stack

C code:

/* Echo Line */  
void echo()  
{  
    char buf[4];  /* Way too small! */  
    gets(buf);  
    puts(buf);  
}

Assembly code:

echo:
  subq  $24, %rsp
  movq  %rsp, %rdi
  call  gets
  . . .

Pasted image 20251023162041.png|250

Buffer Overflow Example

Pasted image 20251028131153.png|500

Buffer Overflow Example #1

Pasted image 20251028131217.png|500

Buffer Overflow Example #2

Pasted image 20251028131405.png|500

Buffer Overflow Example #3

Pasted image 20251028131430.png|500

Buffer Overflow Example #3 Explained

Pasted image 20251028131539.png|450

Code Injection Attacks

Pasted image 20251028131726.png|500

Notes:

Exploits Based on Buffer Overflows

Example: the original Internet worm (1988)

Notes:

Example 2: IM War

Aside: Worms and Viruses

Ok, what to do about buffer overflow attacks

1. Avoid Overflow Vulnerabilities in Code (!)

/* Echo Line */  
void echo()  
{  
    char buf[4];  /* Way too small! */  
    fgets(buf, 4, stdin);  
    puts(buf);  
}

2. System-Level Protections can help (Randomized offsets)

Pasted image 20251028133618.png|150

2. System-Level Protections can help (Nonexecutable code segments)

Pasted image 20251028133929.png|300

3. Stack Canaries can help

Protected Buffer Disassembly (skip)

echo:

  40072f:	sub    $0x18,%rsp
  400733:	mov    %fs:0x28,%rax
  40073c:	mov    %rax,0x8(%rsp)
  400741:	xor    %eax,%eax
  400743:	mov    %rsp,%rdi
  400746:	callq  4006e0 <gets>
  40074b:	mov    %rsp,%rdi
  40074e:	callq  400570 <puts@plt>
  400753:	mov    0x8(%rsp),%rax
  400758:	xor    %fs:0x28,%rax
  400761:	je     400768 <echo+0x39>
  400763:	callq  400580 <__stack_chk_fail@plt>
  400768:	add    $0x18,%rsp
  40076c:	retq 

Setting Up canary

C code:

/* Echo Line */  
void echo()  
{  
    char buf[4];  /* Way too small! */  
    gets(buf);  
    puts(buf);  
}

Assembly code:

echo:
	. . .
	movq	%fs:40, %rax  # Get canary
	movq	%rax, 8(%rsp) # Place on stack
	xorl	%eax, %eax    # Erase canary
	. . .

Pasted image 20251028134456.png|200

Checking Canary

Assembly code:

echo:
	. . .
	movq	8(%rsp), %rax     # Retrieve from stack
	xorq	%fs:40, %rax      # Compare to canary
	je	    .L6               # If same, OK
	call	__stack_chk_fail  # FAIL
.L6:        . . .

Input: 0123456

Pasted image 20251103201253.png|200

Return-Oriented Programming Attacks

Notes:

Why ROP defeats NX but not always other defenses

Gadget Example #1

Pasted image 20251028134635.png|550

Gadget Example #2

Pasted image 20251103203338.png|575

ROP Execution

Pasted image 20251103203614.png|500

Unions

"Like structs, except that all the elements occupy the same space"

Union Allocation

Pasted image 20251028135101.png|600

Using Union to Access Bit Patterns

Pasted image 20251028135350.png|550

Byte Ordering Revisited

Byte Ordering Example

union {
      unsigned char c[8];
      unsigned short s[4];
      unsigned int i[2];
      unsigned long l[1];
    } dw;

Pasted image 20251028135625.png|475

Print function:

int j;
for (j = 0; j < 8; j++)
    dw.c[j] = 0xf0 + j;

printf("Characters 0-7 ==  [0x%x,0x%x,0x%x,0x%x,0x%x,0x%x,0x%x,0x%x]\n",
    dw.c[0], dw.c[1], dw.c[2], dw.c[3],
    dw.c[4], dw.c[5], dw.c[6], dw.c[7]);

printf("Shorts 0-3 == [0x%x,0x%x,0x%x,0x%x]\n",
    dw.s[0], dw.s[1], dw.s[2], dw.s[3]);

printf("Ints 0-1 == [0x%x,0x%x]\n",
    dw.i[0], dw.i[1]);

printf("Long 0 == [0x%lx]\n",
    dw.l[0]);

Byte Ordering on IA32 (Little Endian)

Little Endian
Pasted image 20251103210713.png|425
Output:

Characters 0-7 == [0xf0,0xf1,0xf2,0xf3,0xf4,0xf5,0xf6,0xf7]
Shorts     0-3 == [0xf1f0,0xf3f2,0xf5f4,0xf7f6]
Ints       0-1 == [0xf3f2f1f0,0xf7f6f5f4]
Long       0   == [0xf3f2f1f0]

Byte Ordering on Sun (Big Endian)

Big Endian
Pasted image 20251103210845.png|425
Output:

Characters 0-7 == [0xf0,0xf1,0xf2,0xf3,0xf4,0xf5,0xf6,0xf7]
Shorts     0-3 == [0xf0f1,0xf2f3,0xf4f5,0xf6f7]
Ints       0-1 == [0xf0f1f2f3,0xf4f5f6f7]
Long       0   == [0xf0f1f2f3]

Byte Ordering on x86-64 (Little Endian 64-bit)

Little Endian
Pasted image 20251103210944.png|425
Output:

Characters 0-7 == [0xf0,0xf1,0xf2,0xf3,0xf4,0xf5,0xf6,0xf7]
Shorts     0-3 == [0xf1f0,0xf3f2,0xf5f4,0xf7f6]
Ints       0-1 == [0xf3f2f1f0,0xf7f6f5f4]
Long       0   == [0xf7f6f5f4f3f2f1f0]

Summary of Compound Types in C