I am learning x86 assembly out of curiosity. I'm currently using a Linux based OS with the NASM assembler. I am having a difficult time understanding why
SECTION .text
global _start
_start:
nop
mov ebx, 25
mov [0xFFF], ebx
;Exit the program
mov eax, 1
mov ebx, 0
int 0x80
Would lead to a segmentation fault (when moving the contents of the ebx register to memory location 0xFFF). I was thinking that building a program in pure asm would give me unrestricted access to my process' virtual address space. Is this not the case?
How would you implement something like a heap in assembly?
On Linux(x86) - although you have a virtual address range of 4gb in your process, not all of it is accessible. The upper 1gb is where the kernel resides, and there are areas of low memory that can't be used. Virtual memory address 0xfff can't be written to or read from (by default) so your program crashes with a segfault.
In a followup comment you suggested you were intending to create a heap in assembler. That can be done, and one method is to use the sys_brk system call. It is accessed via int 0x80 and EAX=45 . It takes a pointer in EBX representing the new top of the heap. Generally the bottom of the heap area is initialized to the area just beyond your programs data segment(above your program in memory). To get the address of the initial heap location you can call sys_break with EBX set to 0. After the system call EAX will be the current base pointer of the heap. You can save that away when you need to access your heap memory or allocate more heap space.
This code provides an example for purposes of clarity (not performance), but might be a starting point to understanding how you can manipulate the heap area:
SECTION .data
heap_base: dd 0 ; Memory address for base of our heap
SECTION .text
global _start
_start:
; Use `brk` syscall to get current memory address
; For the bottom of our heap This can be achieved
; by calling brk with an address (EBX) of 0
mov eax, 45 ; brk system call
xor ebx, ebx ; don't request additional space, we just want to
; get the memory address for the base of our processes heap area.
int 0x80
mov [heap_base], eax ; Save the heap base
;Now allocate some space (8192 bytes)
mov eax, 45 ; brk system call
mov ebx, [heap_base] ; ebx = address for base of heap
add ebx, 0x2000 ; increase heap by 8192 bytes
int 0x80
; Example usage
mov eax, [heap_base] ; Get pointer to the heap's base
mov dword [eax+0xFFF], 25 ; mov value 25 to DWORD at heapbase+0xFFF
;Exit the program
mov eax, 1
xor ebx, ebx
int 0x80
You don't have unrestricted RAM. Furthermore, you don't have unrestricted access to the part of your address space which is backed by RAM. Code pages are mapped read-only. And as a ring-3 program, you can't change that yourself.
Related
Theoretical question. But let's say I have written an assembly program. I have "labelx:" I want the program to read at this memory address and only this size and print to stdout.
Would it be something like
jmp labelx
And then would i then use the Write syscall , making sure to read from the next instruction from labelx:
mov rsi,rip
mov rdi,0x01
mov rdx,?
mov rax,0x01
syscall
to then output to stdout.
However how would I obtain the size to read itself? Especially if there is a
label after the code i want to read or code after. Would I have to manually
count the lines?
mov rdx,rip+(bytes*lines)
And then syscall with populated registers for the syscall to write to from rsi to rdi. Being stdout.
Is this Even possible? Would i have to use the read syscall first, as the write system call requires rsi to be allocated memory buffer. However I assumed .text is already allocated memory and is read only. Would I have to allocate onto the stack or heap or a static buffer first before write, if it's even possible in the first place?
I'm using NASM syntax btw. And pretty new to assembly. And just a question.
Yes, the .text section is just bytes in memory, no different from section .rodata where you might normally put msg: db "hello", 10. x86 is a Von Neumann architecture (not Harvard), so there's no distinction between code pointers and data pointers, other than what you choose to do with them. Use objdump -drwC -Mintel on a linked executable to see the machine-code bytes, or GDB's x command in a running process, to see bytes anywhere.
You can get the assembler to calculate the size by putting labels at the start/end of the part you want, and using mov edx, prog_end - prog_start in the code at the point where you want that size in RDX.
See How does $ work in NASM, exactly? for more about subtracting two labels (in the same section) to get a size. (Where $ is an implicit label at the start of the current line, although $ isn't likely what you want here.)
To get the current address into a register, you need a RIP-relative LEA, not mov, because RIP isn't a general-purpose register and there's no special form of mov that reads it.
here:
lea rsi, [rel here] ; with DEFAULT REL you could just use [here]
mov edi, 1 ; stdout fileno
mov edx, .end - here ; assemble-time constant size calculation
mov eax, 1 ; __NR_write
syscall
.end:
This is fully position-independent, unlike if you used mov esi, here. (How to load address of function or label into register)
The LEA could use lea rsi, [rel $] to assemble to the same machine-code bytes, but you want a label there so you can subtract them.
I optimized your MOV instructions to use 32-bit operand-size, implicitly zero-extending into the full 64-bit RDX and RAX. (And RDI, but write(int fd, void *buf, size_t len) only looks at EDI anyway for the file descriptor).
Note that you can write any bytes of any section; there's nothing special about having a block of code write itself. In the above example, put the start/end labels anywhere. (e.g. foo: and .end:, and mov edx, foo.end - foo taking advantage of how NASM local labels work, by appending to the previous non-local label, so you can reference them from somewhere else. Or just give them both non-dot names.)
Assuming an instruction accesses memory and stores a register's content into it, on x86_64 architecture. For Example,
mov rax, [rip + offset1]
My question is, if the segmentation is enabled, and the access to same memory address was done with segment register, will it incur more overhead(latency or throughput)?For Example,
mov rax, gs:[rip + offset2]
Is it possible to make 64bit Linux loader to limit the address space of the loaded 32bit program to some upper limit?
Or to set some holes in the address space that to not be allocated by the kernel?
I mean for specific executable, not globally for all processes, neither through kernel configuration. Some code or ELF executable flags are examples of appropriate solution.
The limit should be forced for all loaded shared libraries as well.
Clarification:
The problem I want to fix is that my code uses the numbers above 0xc0000000 as a handle values and I want to clearly distinct between handle values and memory addresses, even when the memory addresses are allocated and returned by some third party library function.
As long as the address space in 64bit Linux is very close to 4G limit, there is no enough addressing space left for the handle values.
On the other hand 3GB or even less is far enough for all my needs.
OK, I found the answer of this question elsewhere.
The solution is to change the "personality" of your program to PER_LINUX32_3GB, using the Linux system call sys_personality.
But there is a problem. After switching to PER_LINUX32_3GB Linux kernel will not allocate space in the upper 1GB, but the already allocated space, for example the application stack, remains there.
The solution is to "restart" your program through sys_execve system call.
Here is the code where I packed everything in one:
proc ___SwitchLinuxTo3GB
begin
cmp esp, $c0000000
jb .finish ; the system is native 32bit
; check the current personality.
mov eax, sys_personality
mov ebx, -1
int $80
; and exit if it is what intended
test eax, ADDR_LIMIT_3GB
jnz .finish ; everything is OK.
; set the needed personality
mov eax, sys_personality
mov ebx, PER_LINUX32_3GB
int $80
; and restart the process
mov eax, [esp+4] ; argument count
mov ebx, [esp+8] ; the filename of the executable.
lea ecx, [esp+8] ; the arguments list.
lea edx, [ecx+4*eax+4] ; the environment list.
mov eax, sys_execve
int $80
; if something gone wrong, it comes here and stops!
int3
.finish:
return
endp
I am confused towards why/how a value gets printed in x86 assembly in a Linux environment.
For example if I wish to print a value I would do this:
mov eax, 4
mov ebx, 1
mov ecx, msg
mov edx msgLength
int 80h
Now I understand the numerical value 4 will make the system call to sys_write after the interrupt. But my question is, what is the significance of the 4? Is it loading the address of the decimal value 4 into eax? Or is it loading the value 4 into the eax register?
I am confused after reading I can transfer the value at an address to a register using the following instruction:
mov eax, [msg]
eax will now contain the bytes at the address of msg, but I would guess this format is not acceptable:
mov eax, [4]
So what is really happening when I move 4 into eax to print something?
Simply the value (number) 4 is loaded into eax, no magic there. The operating system will look at the value in eax to figure out what function you want. System call number is a code that identifies the various available kernel functions you can use.
Linux kernel maintains all the system call routines as an array of function pointers (can be called as sys_call table) and the value in the eax gives the index to that array (which system call to choose) by the kernel. Other registers like ebx, ecx, edx contains the appropriate parameters for that system call routine.
And the int 80h is for software interrupt to the cpu from user mode to kernel mode because actual system call routine is kernel space function.
how do i store 2 digit data using extra segment(es) and appropriate offset....i tired this way..
SECTION .data
offset : db 0100H
data : db 56H
SECTION .bss
.......(initializations not required in this program)
SECTION .text
global main
main :
mov ax,1000H
mov es,ax
mov si,[offset]
mov al,[data]
mov es:[si],al
mov rdi,0
mov rax,60
syscall
What is wrong in this? I'm completely new in nasm programming...
Directly accessing a certain memory address using the segent pointers only work in 16 bit real mode. In 64 bit protected mode the physical memory addresses are mapped to application visible addresses by the MMU. This is typically managed by the OS while user space applications are simply not allowed to temper with it. If you want to access certain physical memory you need to tell the OS map it into your memory space somehow.