Process Memory

1. Stack

1.1. Stack Pointer

1.1.1. Push & Pop Instructions

File main.s

.section .text
.global _start

.text
_start:
    movabs  $0xabcdabcdabcdabcd, %rax
    push    %rax

    movabs  $0x1234123412341234, %rax
    push    %rax

    movq    $60, %rax     # syscall: exit
    xorq    %rdi, %rdi    # exit code 0
    syscall

Build.

as main.s -o main.o
ld main.o -o main

Debug.

(gdb) file main
(gdb) break _start
(gdb) run
Breakpoint 1, 0x0000000000401000 in _start ()

Monitor stack.

(gdb) disassemble
Dump of assembler code for function _start:
=> 0x0000000000401000 <+0>:     movabs $0xabcdabcdabcdabcd,%rax
   0x000000000040100a <+10>:    push   %rax
   0x000000000040100b <+11>:    movabs $0x1234123412341234,%rax
   0x0000000000401015 <+21>:    push   %rax
   0x0000000000401016 <+22>:    mov    $0x3c,%rax
   0x000000000040101d <+29>:    xor    %rdi,%rdi
   0x0000000000401020 <+32>:    syscall

(gdb) while 1
> x/4gx $rsp
> x/i $rip
> nexti
> end
0x7fffffffe700: 0x0000000000000001      0x00007fffffffe9ab
0x7fffffffe710: 0x0000000000000000      0x00007fffffffe9bc

=> 0x401000 <_start>:   movabs $0xabcdabcdabcdabcd,%rax
=> 0x40100a <_start+10>:        push   %rax
0x7fffffffe6f8: 0xabcdabcdabcdabcd      0x0000000000000001
0x7fffffffe708: 0x00007fffffffe9ab      0x0000000000000000

=> 0x40100b <_start+11>:        movabs $0x1234123412341234,%rax
=> 0x401015 <_start+21>:        push   %rax
0x7fffffffe6f0: 0x1234123412341234      0xabcdabcdabcdabcd
0x7fffffffe700: 0x0000000000000001      0x00007fffffffe9ab

Illustrate.

┌───────────────┬───────────────────┐        ┌───────────────┬───────────────────┐        ┌───────────────┬───────────────────┐
│   address     │       value       │        │   address     │       value       │        │   address     │       value       │
├───────────────┼───────────────────┤        ├───────────────┼───────────────────┤        ├───────────────┼───────────────────┤
│               │                   │        │               │                   │        │               │                   │
│               │                   │ push   │               │                   │ push   │0x7fffffffe6f00x1234123412341234
│               │                   │ ─────► │0x7fffffffe6f80xabcdabcdabcdabcd│ ─────► │0x7fffffffe6f80xabcdabcdabcdabcd
0x7fffffffe7000x0000000000000001│        │0x7fffffffe7000x0000000000000001│        │0x7fffffffe7000x0000000000000001
0x7fffffffe7080x00007fffffffe9ab│        │0x7fffffffe7080x00007fffffffe9ab│        │0x7fffffffe7080x00007fffffffe9ab
0x7fffffffe7100x0000000000000000│        │0x7fffffffe7100x0000000000000000│        │0x7fffffffe7100x0000000000000000
0x7fffffffe7180x00007fffffffe9bc│        │0x7fffffffe7180x00007fffffffe9bc│        │0x7fffffffe7180x00007fffffffe9bc
└───────────────┴───────────────────┘        └───────────────┴───────────────────┘        └───────────────┴───────────────────┘

1.1.2. Call & Ret Instructions

File main.s

.section .text
.global _start

_start:    
    mov     $5, %rdi    # x = 5
    mov     $7, %rsi    # y = 7
    call    sum         # sum(x, y)

    mov     $60, %rax   # syscall: exit
    xor     %rdi, %rdi  # exit code 0
    syscall

# add two integers
sum:
    add     %rsi, %rdi  # x += y
    mov     %rdi, %rax  # rax = x
    ret
as main.s -o main.o
ld main.o -o main
(gdb) file main
(gdb) break _start
(gdb) run
Breakpoint 1, 0x0000000000401000 in _start ()

Monitor stack.

(gdb) disassemble
Dump of assembler code for function _start:
=> 0x0000000000401000 <+0>:     mov    $0x5,%rdi
   0x0000000000401007 <+7>:     mov    $0x7,%rsi
   0x000000000040100e <+14>:    call   0x40101f <sum>
   0x0000000000401013 <+19>:    mov    $0x3c,%rax
   0x000000000040101a <+26>:    xor    %rdi,%rdi
   0x000000000040101d <+29>:    syscall

(gdb) while 1
> x/4gx $rsp
> x/i $rip
> stepi
> end
0x7fffffffe700: 0x0000000000000001      0x00007fffffffe9ab
0x7fffffffe710: 0x0000000000000000      0x00007fffffffe9bc

=> 0x40100e <_start+14>:        call   0x40101f <sum>
0x7fffffffe6f8: 0x0000000000401013      0x0000000000000001
0x7fffffffe708: 0x00007fffffffe9ab      0x0000000000000000

=> 0x401025 <sum+6>:    ret    
0x7fffffffe700: 0x0000000000000001      0x00007fffffffe9ab
0x7fffffffe710: 0x0000000000000000      0x00007fffffffe9bc

Illustrate.

┌───────────────┬───────────────────┐        ┌───────────────┬───────────────────┐        ┌───────────────┬───────────────────┐
│   address     │       value       │        │   address     │       value       │        │   address     │       value       │
├───────────────┼───────────────────┤        ├───────────────┼───────────────────┤        ├───────────────┼───────────────────┤
│               │                   │        │               │                   │        │               │                   │
│               │                   │ call   │               │                   │ ret    │               │                   │
│               │                   │ ─────► │0x7fffffffe6f80x0000000000401013│ ─────► │               │                   │
0x7fffffffe7000x0000000000000001│        │0x7fffffffe7000x0000000000000001│        │0x7fffffffe7000x0000000000000001
0x7fffffffe7080x00007fffffffe9ab│        │0x7fffffffe7080x00007fffffffe9ab│        │0x7fffffffe7080x00007fffffffe9ab
0x7fffffffe7100x0000000000000000│        │0x7fffffffe7100x0000000000000000│        │0x7fffffffe7100x0000000000000000
0x7fffffffe7180x00007fffffffe9bc│        │0x7fffffffe7180x00007fffffffe9bc│        │0x7fffffffe7180x00007fffffffe9bc
└───────────────┴───────────────────┘        └───────────────┴───────────────────┘        └───────────────┴───────────────────┘

1.2. Base Pointer

1.2.1. Prolog & Epilog

Example: parent_func calls child_func.

parent_func() {
    child_func()
}

child_func() {

}
<parent_func>:
call    child_func  # save parent next instruction address (rip) to stack,
                    # jump to child_func.
<child_func>:
                    # prolog
push   %rbp         # save parent base pointer (rbp) to stack.
mov    %rsp, %rbp   # move current base pointer to top of stack (rsp).

...                 # function body

                    # epilog
pop    %rbp         # restore parent base pointer from stack.
ret                 # restore parent next instruction address from stack,
                    # jump to this instruction (parent_func).

Illustrate.

        ┌───────────┐              ┌───────────┐             ┌───────────┐             ┌───────────┐
        │   stack   │              │   stack   │             │   stack   │             │   stack   │
$rbp ─┐ ├───────────┤              ├───────────┤    $rbp ─┐  ├───────────┤    $rbp ──┐ ├───────────┤
      │ │           │              │           │          │  │           │           │ │           │
      │ │           │              │           │          │  │           │           │ │           │
      │ │           │              │           │          │  │           │           │ │           │
      │ │           │  parent_func │           │          └─►│parent $rbp│           │ │           │
      │ │           │  call        │parent $rip│             │parent $rip│           │ │           │
      └─┼►          │  child_func  │           │   prolog    │           │  epilog   └─┼►          │
        │           │ ───────────► │           │ ──────────► │           │ ──────────► │           │
        └───────────┘              └───────────┘             └───────────┘             └───────────┘

1.2.2. Arguments & Local Variables

Local variables locates on the stack for each function call. Their addresses are based on negative offsets from the base pointer rbp.

int a, b, c;
a = 1;
b = 2;
c = 3;

movl   $0x1,-0xc(%rbp)  ; a at $rbp-0xc
movl   $0x2,-0x8(%rbp)  ; b at $rbp-0x8
movl   $0x3,-0x4(%rbp)  ; c at $rbp-0x4

When the parent function calls the child function, the arguments are saved in the registers rdi, rsi, rdx, rcx, r8, and r9. If more than 6 arguments, the extra arguments are saved on the stack.
Inside the child function, it reads the registers and saves the arguments into its stack frame. The argument addresses in the stack frame are based on the base pointer rbp.

parent_function()
{
    child_func(a, b);
}

int child_func(int a, int b)
{
}
<parent_function>:
  mov    -0x8(%rbp),%esi    # save a to $esi
  mov    -0xc(%rbp),%edi    # save b to $edi
  call   child_func

<child_func>:
  mov    %edi,-0x14(%rbp)   # save $edi to stack, b
  mov    %esi,-0x18(%rbp)   # save $esi to stack, a

1.3. Examine Stack Frame

So:

In GDB:

───low address  ┌────────────────┐
                │     stack      │
                ├────────────────┤
                │                │
...
                │ argument       │
                │ argument       │
...
                │ local variable │
                │ local variable │
      $rbp ───► │ parent $rbp    │
                │ parent %rip    │
                │                │
──high address  └────────────────┘

File main.c

int func(int argv1, int argv2)
{
    int a = 0xaaaaaaaa;
    int b = 0xbbbbbbbb;
    int c = 0xcccccccc;
    int d = 0xdddddddd;
    return 0;
}

int main(int argc, char** argv)
{
    func(0x11111111, 0x22222222);
    return 0;
}
$ gcc main.c -o main

Run under GDB.

(gdb) file main
(gdb) disassemble func
   0x0000000000001129 <+0>:     endbr64 
   0x000000000000112d <+4>:     push   %rbp
   0x000000000000112e <+5>:     mov    %rsp,%rbp
   0x0000000000001131 <+8>:     mov    %edi,-0x14(%rbp)
   0x0000000000001134 <+11>:    mov    %esi,-0x18(%rbp)
   0x0000000000001137 <+14>:    movl   $0xaaaaaaaa,-0x10(%rbp)
   0x000000000000113e <+21>:    movl   $0xbbbbbbbb,-0xc(%rbp)
   0x0000000000001145 <+28>:    movl   $0xcccccccc,-0x8(%rbp)
   0x000000000000114c <+35>:    movl   $0xdddddddd,-0x4(%rbp)
   0x0000000000001153 <+42>:    mov    $0x0,%eax
   0x0000000000001158 <+47>:    pop    %rbp
   0x0000000000001159 <+48>:    ret

Set breakpoint at the end of func.

(gdb) break *func+42

(gdb) run
Breakpoint 1, 0x0000555555555153 in func ()

Examine arguments and local variables.

(gdb) x/-6wx $rbp
0x7fffffffe5b8: 0x22222222      0x11111111      0xaaaaaaaa      0xbbbbbbbb
0x7fffffffe5c8: 0xcccccccc      0xdddddddd

Examine parent’s $rip and $rbp.

(gdb) x/2a $rbp
0x7fffffffe5d0: 0x7fffffffe5f0  0x55555555517c <main+34>

Illustrate the stack.

                         ┌────────────────┐               
                         │     stack      │               
          ───low address ├────────────────┤               
                         │                │               
          0x7fffffffe5b80x22222222     │ argument argv2
0x11111111     │ argument argv2
0xaaaaaaaa     │ variable a    
0xbbbbbbbb     │ variable b    
          0x7fffffffe5c80xcccccccc     │ variable c    
0xdddddddd     │ variable d    
$rbp ───► 0x7fffffffe5d00x7fffffffe5f0 │ parent $rbp   
0x55555555517c │ parent $rip   
                         │                │               
          ──high address └────────────────┘               


2. GDB

Find Memory

Search memory for the sequence of bytes.

  find [/sn] start_addr, +len, vals...
  find [/sn] start_addr, end_addr, vals...
int main() 
{
  char str[] = "hello-hello";
  short a = 0x0102;
  int b = 0x0a0b0c0d;
}
# find str
(gdb) find &str, +sizeof(str), "hello"
(gdb) find &str, +sizeof(str), 'h', 'e', 'l', 'l', 'o'

# find a
(gdb) find &a, +2, (short)0x0102
(gdb) find /h &a, +2, 0x0102
(gdb) find /b &a, +2, 0x02, 0x01

# find b
(gdb) find &b, +4, (int)0x0a0b0c0d
(gdb) find /w &b, +4, 0x0a0b0c0d

# find b, a
#                  (int)b         , padding      , (short)a
(gdb) find &b, +8, (int)0x0a0b0c0d, (short)0x0000, (short)0x0102


Export Memory

Dump the contents of memory from start_addr to end_addr, or the value of expr, to file.

  dump [format] memory filename start_addr end_addr
  dump [format] value filename expr
(gdb) p &str
$3 = (char (*)[12]) 0xffffffffec88

(gdb) dump memory mem.hex 0xffffffffec88 0xffffffffec88+12
(gdb) dump value val.hex str


$ hexdump -C mem.hex
00000000  68 65 6c 6c 6f 2d 68 65  6c 6c 6f 00              |hello-hello.|
$ hexdump -C val.hex 
00000000  68 65 6c 6c 6f 2d 68 65  6c 6c 6f 00              |hello-hello.|


Import Memory

Restore the contents of file filename into memory.

  restore file [binary] offset start end
(gdb) p &str
$3 = (char (*)[12]) 0xffffffffec88

#             file           start
(gdb) restore mem.hex binary 0xffffffffec88
Restoring binary file mem.hex into memory (0xffffffffec88 to 0xffffffffec94)

#             file           offset     start
(gdb) restore mem.hex binary 0          0xffffffffec88
Start address is greater than length of binary file mem.hex.


Memory Regions

List memory regions.

  info proc mapping


Sample: Add a memory region by gdb.

int main()
{
  return 0
}
hello world
(gdb) set $fd = (int) open("hello.txt", 0)

(gdb) set $region_addr = (void*) mmap(0, 4096, 1, 1, $fd, 0)

(gdb) info proc mappings
Start Addr         End Addr           Size    Offset  Perms File 
0x0000fffff7ff5000 0x0000fffff7ff6000 0x1000  0x0     r--s  /root/demo/hello.txt 
...

(gdb) x/s $region_addr
0xfffff7ff5000: "hello world\n"
    


Core Dump

Generate the core dump.

  gcore [file]

Load the core dump.

  gdb program core