1

I'm writing in assembly and I'm trying to figure out how to perform an execve syscall, but instead of having the output printed to the terminal, I'd like to know where it's stored so I can use it later, kind of like piping commands.

For instance, here's assembly for executing the command 'which' via execve, essentially executing the command '$ which ls':

GLOBAL _start
SECTION .TEXT

_start:
    XOR         EAX,EAX
    PUSH        EAX
    PUSH        0x68636968 
    PUSH        0x772f6e69 
    PUSH        0x622f7273 
    PUSH        0x752f2f2f 
    MOV         EBX, ESP
    PUSH        EAX
    PUSH        0x736c
    MOV         ESI, ESP
    XOR         EDX, EDX
    PUSH        EDX
    PUSH        ESI
    PUSH        EBX
    MOV         ECX, ESP
    MOV         AL, 0x0B; EXECVE SYSCALL NUMBER
    INT         0x80

Lines 7-10 push the address of /usr/bin/which onto the stack, and line 13 pushes the argument ls onto the stack. It then pushes the arguments array onto the stack and stores that in the ECX, has the EBX pointing to the address of the location of /usr/bin/which, and the EAX set to the syscall number 0xb (11) for the execve syscall. When executed, it returns /bin/ls, the location of ls that we asked it to find.

How do I store that result of /bin/ls somewhere for other use? Like if I wanted to keep writing code and use what's returned here as a part of the next piece of code, how do I keep the returned value in either a register or on the stack?

1 Answers1

0

Basically, you have to do some variation of this:

  1. Use the pipe() system call to make a fifo.
  2. fork() off a child process. In the parent, this returns the child's process ID which you need to remember. In the child, this returns zero.
  3. In the parent, close the writing end of the fifo, in the child close the reading end.
  4. In the child, use dup2() to move the writing end of the pipe to file descriptor 1. Then close the original file descriptor of the pipe.
  5. In the child, execve the program you want to run. Note that execve replaces the current process with a new image, so I am not sure how you expected your previous program to work.
  6. In the parent, read from the pipe until you encounter EOF (i.e. read() returns zero). What you read is the child's standard output. Store that output in a buffer.
  7. Finally, use wait() to collect to child's exit status. If you don't do this, the dead child will linger in the process table as a zombie, taking up resources.

Instead of a pipe, you can also open() a file to write the output to. This has the advantage that you can use fstat() to find out how much output the process wrote. However, you then need to implement logic similar to the tmpfile function to create and open a temporary file.

fuz
  • 88,405
  • 25
  • 200
  • 352