Assembly x86 brk() call use
Asked Answered
G

4

10

I am trying to dynamically allocate memory into the heap and then assign values in those memory addresses. I understand how to allocate the memory but how would I assign for example the value in a register to that first dynamic memory address? This is what I have so far:

    push rbp
    mov rbp, rsp            ;initialize an empy stack to create activation records for the rest of the subroutines                                                                                                                        

    mov rax, 0x2d           ;linux system call for brk()                                                                                                                                                                                  
    mov rbx, 0x0            ;to get the adress of the first adress we are allocating we must have 0 in rbx                                                                                                                                
    int 0x80                ;calls the linux operating system kernel for assistance                                                                                                                                                       
    mov [brk_firstLocation], rax ;the first position in the heap will be returned in rax thus i save the first loaction in a varable called brk_firstLocation                                                                             

    mov rbx, rax            ;the memory adress of the start of the heap is moved in rbx                                                                                                                                                   
    add rbx, 0x14           ;we want 5 bytes worth of data alocated in the heap, so the start adress plus 20 bits                                                                                                                         
    mov rax, 0x2d           ;linux system call for brk()                                                                                                                                                                                  
    int 0x80                ;calls the linux operating system kernel for assistance

What would I do, for example, to mov the value in rax into brk_firstLocation

Gefen answered 23/3, 2014 at 2:59 Comment(0)
S
11

others have pointed out a few things that are wrong with your code. I would like to add that you would not add 20 bits to the current breakpoint (or 20 bytes like add rbx, 20 actually does), you would simply add 5 bytes.

Also, your first syscall argument will not be in rbx, it will be in rdi. The 64-bit syscall ABI uses different system call numbers, different registers, and a different instruction (syscall instead of int 0x80) than the 32-bit ABI (which is still available in 64-bit processes). See also the tag wiki for more ABI links.

Here's how your code should look:

push rbp
mov rbp, rsp

;; sys_brk(0)
mov   rax, 12         ; 12 is SYS_brk (/usr/include/asm/unistd_64.h)
mov   rdi, 0          ; rdi for first syscall arg in the 64-bit ABI, not rbx
syscall               ; syscall, not int 0x80, for the 64-bit ABI

mov   qword [brk_firstLocation], rax

;; sys_brk(old_break + 5)
lea   rdi, [rax + 5]  ; add 5 bytes to the break point
mov   rax, 12
syscall               ; set the new breakpoint

At this point you can use brk_firstLocation as a pointer to whatever 5 byte struct you want to store on the heap. Here's how you would put values in that memory space:

mov   rdi, [brk_firstLocation]   ; load the pointer from memory, if you didn't already have it in a register

mov   byte [rdi], 'A'            ; a char in the first byte
mov   [rdi+1], ecx               ; a 32-bit value in the last 4 bytes.
Succinylsulfathiazole answered 3/7, 2017 at 2:11 Comment(2)
Finally, an answer that uses the 64-bit system call ABI, and uses it correctly. (stackoverflow.com/questions/2535989/…).Planchette
I fixed a couple simple but serious bugs; I think this is now fully correct (and the only correct answer on this question).Planchette
F
2

int 80h is only for 32 bit system calls. Use syscall for 64 bit instead.

Calling sys_brk twice is redundant - in assembly you always know where your program data ends. Simply put a label there and you will have the address.

Allocating this way memory less than one page is pointless, it will be allocated in blocks of 4KB anyway.

It is important to be understood - sys_brk is not heap management function. It is low level memory management.

Fant answered 23/3, 2014 at 6:32 Comment(1)
The brk memory area is not immediately after the end of the programs, it is randomised.Star
M
1

There are a number of problems I see:

  • 0x2d is the brk system call on x86 (32 bit); on x86_64 it's 0xc
  • brk sets the end of the data segment; it returns 0 on success and -1 on failure. It does not return "the first position in the heap". That comes from the symbol _end which the linker sets to the end of the uninitialized preallocated data.

So you want something like:

    mov [brk_firstloaction], _end
    mov rbx, [brk_firstlocation]
    add rbx, 0x14         ; space for 5 dwords (20 bytes)
    mov rax, 12
    int 0x80
Miraflores answered 23/3, 2014 at 4:37 Comment(4)
Maybe the C wrapper of sys_brk returns 0 or -1. It is common case for the system calls. Reference: fresh.flatassembler.net/lscr/data/045.htmlFant
The actual system call does indeed return the end of the new memory area or the current end if 0 was given as the arg.Cestar
int 0x80 is always the 32-bit ABI, even in a 64-bit process. The OP needs eax=12 / syscall, with the first arg rdi. See also What are the calling conventions for UNIX & Linux system calls on x86-64.Planchette
Where can I find more info on the _end symbol? Is there a list of symbols I can reference?Macedoine
D
0

As you have done, call once to retrieve the current bottom of heap, then move the top of heap (which is the brk value). However your code is not correct in using the 64-bit register set r.x. If your Linux is 32-bit (as implied by the use of 45 for the syscall number), then you want the 32-bit register set:

mov eax, 45                            ; brk                                                                                                                                                                                  
mov ebx, 0                             ; arg 1: 0 = fail returning brk value in rax                                                                                                                               
int 0x80                               ; syscall
mov dword ptr [brk_firstLocation], rax ; save result
mov eax, 45                            ; brk                                                                                                                                                                                  
mov ebx, 4                             ; arg 1: allocate 4 bytes for a 32-bit int                                                                                                                                
int 0x80                               ; syscall

mov eax, dword ptr [brk_firstLocation] ; reload rax with allocated memory address.
mov dword ptr [eax], 42 ; use result: store 42 in newly allocated storage

Of course you can re-load the saved value for re-use as many times as needed.

Dihybrid answered 23/3, 2014 at 3:21 Comment(2)
Don't we still have a mixup between 32-bit int 0x80 and 64-bit syscall kernel calls here?Monosome
The OP is writing 64-bit code, so using the 64-bit system call ABI is essential for system calls involving pointers. It's totally different from the i386 ABI (which is still available to 64-bit processes). https://mcmap.net/q/19964/-x86_64-assembly-linux-system-call-confusionPlanchette

© 2022 - 2024 — McMap. All rights reserved.