Segmentation Fault when shutting down Linux with assembly application - linux

The following application generates a Segmentation Fault when executed:
.set __NR_reboot, 169
.set LINUX_REBOOT_CMD_POWER_OFF, 0x4321FEDC
.section .text
.globl _start
_start:
movl $LINUX_REBOOT_CMD_POWER_OFF, %ebx
movl $__NR_reboot, %eax
int $0x80
It's a quite simple application and I must be missing something really obvious. Can someone help me?
It was compiled with:
as shutdown.s -o shutdown.o
ld shutdown.o -o shutdown
EDIT:
Even a simple application that just calls syscall sync() generates a Segmentation Fault:
.set __NR_sync, 36
.section .text
.globl _start
_start:
movl $__NR_sync, %eax
int $0x80
movl $1, %eax #syscall exit
movl $0, %eax
int $0x80

WARNING: remember to sync(2) before calling reboot(2).
The reboot(2) system call takes 4 parameters.You are confusing it with the libc
wrapper.
WARNING: remember to sync(2) before calling reboot(2).
(It actually takes the magic* parameters so that people have to reread the documentation and don't forget calling sync(2).)
WARNING: Did I say that you have to sync(2) before calling reboot(2)?

I'm adding the final & working source code as this question might interest somebody in the future:
# For the right sys_call numbers on your arch,
# check <asm/unistd_32.h> (or unistd_64.h)
.set __NR_sync, 36 # sys_call sync()
.set __NR_reboot, 88 # sys_call reboot()
.set LINUX_REBOOT_MAGIC1, 0xfee1dead # flags are specified in: <linux/reboot.h>
.set LINUX_REBOOT_MAGIC2, 672274793
.set LINUX_REBOOT_CMD_POWER_OFF, 0x4321fedc
.set LINUX_REBOOT_CMD_RESTART, 0x01234567
.section .text
.globl _start
_start:
movl $__NR_sync, %eax # call sync()
int $0x80
movl $__NR_reboot, %eax
movl $LINUX_REBOOT_MAGIC1, %ebx
movl $LINUX_REBOOT_MAGIC2, %ecx
movl $LINUX_REBOOT_CMD_RESTART, %edx
#movl $0, %esi
int $0x80 # call reboot()
movl $1, %eax
movl $0, %ebx
int $0x80 # call exit()

From linux/i386/syscall.S: The function number should be placed in %eax and any arguments in the following registers in order: %ebx, %ecx, %edx, %esi, %edi, and %ebp.
Which is why the last movl %eax,0 in the code should be changed to movl %ebx, 0.

Related

Global variable get corrupted after software interrupt

I'm currently learning x86 assembly language at school, so I could ask a dumb question (though I found nothing useful).
I have some global variables declared in the data section in the main file, and then I have a second file where there are two functions that use these global variables.
.section .data
.globl file_desc
.globl init
.globl reset
.globl rpm
file_desc: .long
init: .int
reset: .int
rpm: .long
In the _start section, I call the sys_open syscall for getting the file descriptor and I save it in the file_desc variable.
Then I call a function in the other file:
read_init:
# read the INIT and convert it
movl $SYS_READ, %eax
movl file_desc, %ebx
leal init, %ecx
movl $1, %edx
int $0x80
cmp $0, %eax # check for EOF
jle eof
jmp get_init
eof:
movl $47, init # we make the init to -1 xD
get_init:
subb $48, init # get the real value of INIT
# skip 1 byte
movl $SYS_SEEK, %eax
movl file_desc, %ebx
movl $1, %ecx
movl $1, %edx
int $0x80
ret
The problem is that when I reach the sys_lseek syscall, file_desc has been modified! I debugged with GDB to see it and after calling the read syscall the value is not the same.
In theory it shouldn't modify it, so what the heck is doing my program?

X86: making system calls without referring the standard library

I wrote the code that reads from stdin and writes to the stdout:
#include <stdio.h>
#include <unistd.h>
int main() /* copy input to output */
{
char buf[BUFSIZ];
int n;
while ((n = read(0, buf, BUFSIZ)) > 0)
write(1, buf, n);
return 0;
}
After I converted into the assembly code (a .s file) in 32-bit AT&T syntax:
.text
.globl _start
_start:
pushl %ebp
movl %esp, %ebp
andl $-16, %esp #16 bit alignment
subl $8224, %esp #space for local variables
jmp _READ
_WRITE:
movl 8220(%esp), %eax
movl %eax, 8(%esp)
leal 28(%esp), %eax
movl %eax, 4(%esp)
movl $1, (%esp)
call write
int $0x80
_READ:
movl $8192, 8(%esp) #buffer length
leal 28(%esp), %eax
movl %eax, 4(%esp)
movl $0, (%esp)
call read
movl %eax, 8220(%esp)
cmpl $0, 8220(%esp)
jg _WRITE
movl $0, %eax
leave
ret
It works fine, but I'm not sure how to making the "read" and "write" system calls using plain assembly(i.e. moving numbers into certain registers and use "int 0x80" to execute the system calls).
My goal is to make it work even if it is compiled with the "-nostdlib" option.
Hint: 32-bit x86 is old, slow, weird and deprecated. You should use amd64 instead.
The list of system calls for Linux i386 is available in Linux source code:
https://github.com/torvalds/linux/blob/master/arch/x86/entry/syscalls/syscall_32.tbl
Or in glibc headers in asm/unistd_32.h. You can and should #include <asm/unistd.h> so you can use $__NR_write instead of $4 to make your asm source code self-documenting.
The system call number goes in eax. Parameter sequence is always ebx, ecx, edx, esi, edi, ebp. So code becomes:
.text
.globl _start
_start:
pushl %ebp
movl %esp, %ebp
andl $-16, %esp #16 bit alignment
subl $8224, %esp #space for local variables
jmp _READ
_WRITE:
movl 8220(%esp), %edx
leal 28(%esp), %ecx
movl $1, %ebx
movl $4, %eax
int $0x80
_READ:
movl $8192, %edx #buffer length
leal 28(%esp), %ecx
movl $0, %ebx
movl $3, %eax
int $0x80
movl %eax, 8220(%esp)
cmpl $0, 8220(%esp)
jg _WRITE
movl $1, %eax
movl $0, %ebx
int $0x80
Assemble and link with:
$ as --32 hel.s -o hel.o
$ ld -melf_i386 hel.o -o hel
http://www.linuxjournal.com/article/4048
See also
What are the calling conventions for UNIX & Linux system calls on i386 and x86-64
https://blog.packagecloud.io/eng/2016/04/05/the-definitive-guide-to-linux-system-calls/

Assembler SegFail

So guys I got problem . I'm trying just to change one letter in this easy string "Hello World"
so I'm trying assign value to memory cell like this
.section .text
string:
.ascii "Hello, world!"
length:
.quad . -string #Dot = 'here'
.globl _start #Make entry point visible to linker
_start:
movl $4, %eax #4=write
movl $1, %ebx #1=stdout
movl $string, %ecx
movl length, %edx
int $0x80 #Call Operating System
movl $98,4(%ecx) #Critical moment why cannot I do this?
movl $98,string(,%ebx,1) #cannot do this too ;(
movl $4, %eax #4=write
movl $1, %ebx #1=stdout
movl $string, %ecx
movl length, %edx
int $0x80
movl $0, %ebx #Make program return syscall exit status
movl $1, %eax #1=exit
int $0x80 #Call System Again
and additional info I'm working at x64(linux) and assembling it in emulation mode x32 by linux gas so should be all right , TY for replies
You have put your string into the .text section, which is read-only. You should put it into the .data section (but don't forget to switch back to .text for the program code).

Linux x86 assembly printing problems

I wrote this to print argv[0] in x86:
.section .data
newline: .int 0xa, 0
.section .text
.globl _start
_start:
sub %al, %al
movl 4(%esp), %edi /* Pointer to argv[0]. */
sub %ecx, %ecx /* Set %ecx to 0.*/
not %ecx /* Set %ecx to -1.*/
repne scasb /* Search for %al over and over.*/
not %ecx /* Set %ecx to |%ecx| - 1.*/
dec %ecx
movl %ecx, %edx /* Move the strlen of argv[0] into %edx.*/
movl $4, %eax
movl $1, %ebx
movl 4(%esp), %ecx
int $0x80
movl $newline, %ecx
movl $1, %edx
int $0x80
movl $1, %eax
movl $0, %ebx
int $0x80
When I run this file ("print"), the output is this:
[08:27 assembly]$ ./print test
./print[08:30 assembly]$
When I ran this through gdb, the actual string length held in edx is 27, and the string it's checking is "/home/robert/assembly/print", not "./print". So I changed the %esp offsets to 8, to check argv[1]. With the same command as before, the output is this:
test
[08:33 assembly]$
Why does checking argv[0] cause the strange output, when argv[1] does as expected?
I think gdb is "helping" you by adding the full path to argv[0]. After printing, %eax holds the number of characters printed, so you'll want to reload %eax for sys_write again to print the $newline (%ebx should still be okay) - by luck, "test" is the right length. Lord knows what system call you're getting with that longer string!
I'd say you're doing good! (might be a good idea to check argc to make sure argv[1] is there before you try to print it).

sys_execve system call from Assembly

asm_execve.s:
.section .data
file_to_run:
.ascii "/bin/sh"
.section .text
.globl main
main:
pushl %ebp
movl %esp, %ebp
subl $0x8, %esp # array of two pointers. array[0] = file_to_run array[1] = 0
movl file_to_run, %edi
movl %edi, -0x4(%ebp)
movl $0, -0x8(%ebp)
movl $11, %eax # sys_execve
movl file_to_run, %ebx # file to execute
leal -4(%ebp), %ecx # command line parameters
movl $0, %edx # environment block
int $0x80
leave
ret
makefile:
NAME = asm_execve
$(NAME) : $(NAME).s
gcc -o $(NAME) $(NAME).s
Program is executed, but sys_execve is not called:
alex#alex32:~/project$ make
gcc -o asm_execve asm_execve.s
alex#alex32:~/project$ ./asm_execve
alex#alex32:~/project$
Expected output is:
alex#alex32:~/project$ ./asm_execve
$ exit
alex#alex32:~/project$
This Assembly program is supposed to work like the following C code:
char *data[2];
data[0] = "/bin/sh";
data[1] = NULL;
execve(data[0], data, NULL);
Something wrong in system call parameters?
The execve system call is being called, but you are indeed passing it bad parameters.
(You can see this by running your executable using strace.)
There are three problems:
.ascii does not 0-terminate the string. (You might get lucky, as there is nothing following it in your .data section in this example, but that's not guaranteed...) Add a 0, or use .asciz (or .string) instead.
movl file_to_run, %edi moves the value pointed to by the file_to_run symbol into %edi, i.e. the first 4 bytes of the string (0x6e69622f). The address of the string is just the value of the symbol itself, so you need to use the $ prefix for literal values: movl $file_to_run, %edi. Similarly, you need to say movl $file_to_run, %ebx a few lines further down. (This is a common source of confusion between AT&T syntax and Intel syntax!)
The parameters are placed on the stack in the wrong order: -0x8(%ebp) is a lower address than -0x4(%ebp). So the address of the command string should be written to -0x8(%ebp), the 0 should be written to -0x4(%ebp), and the leal instruction should be leal -8(%ebp), %ecx.
Fixed code:
.section .data
file_to_run:
.asciz "/bin/sh"
.section .text
.globl main
main:
pushl %ebp
movl %esp, %ebp
subl $0x8, %esp # array of two pointers. array[0] = file_to_run array[1] = 0
movl $file_to_run, %edi
movl %edi, -0x8(%ebp)
movl $0, -0x4(%ebp)
movl $11, %eax # sys_execve
movl $file_to_run, %ebx # file to execute
leal -8(%ebp), %ecx # command line parameters
movl $0, %edx # environment block
int $0x80
leave
ret
You actually don't need to load anything in the other arguments. If you are doing this in x86 the following simpler code will also work:
.global _main
.section .text
.data
file_to_run:
.asciz "/bin/sh"
.section .text
.globl main
_main:
pushl %ebp
movl %esp, %ebp
movl $11, %eax # sys_execve
movl $file_to_run, %ebx # file to execute
movl $0, %ecx # Null value will work too
movl $0, %edx # Null will works too
int $0x80
leave
ret
This will essentially open a shell terminal after invoking the system call.

Resources