Linux 在汇编中执行execve系统调用后,如何将其输出返回寄存器或堆栈?

Linux 在汇编中执行execve系统调用后,如何将其输出返回寄存器或堆栈?,linux,assembly,system-calls,shellcode,execve,Linux,Assembly,System Calls,Shellcode,Execve,我在汇编中编写代码,试图找出如何执行execve系统调用,但我不想将输出打印到终端,而是想知道它存储在哪里,以便以后可以使用,就像管道命令一样 例如,下面是通过execve执行命令'which'的程序集,实际上是执行命令'$which ls': GLOBAL _start SECTION .TEXT _start: XOR EAX,EAX PUSH EAX PUSH 0x68636968 PUSH 0x

我在汇编中编写代码,试图找出如何执行execve系统调用,但我不想将输出打印到终端,而是想知道它存储在哪里,以便以后可以使用,就像管道命令一样

例如,下面是通过execve执行命令'which'的程序集,实际上是执行命令'$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
第7-10行将
/usr/bin/which
的地址推送到堆栈上,第13行将参数
ls
推送到堆栈上。然后,它将参数数组推送到堆栈上,并将其存储在ECX中,EBX指向
/usr/bin/which
,EAX设置为execve系统调用的系统调用号0xb(11)。执行时,它返回我们要求它查找的
/bin/ls
,即
ls
的位置


如何将
/bin/ls
的结果存储到其他地方以供使用?比如,如果我想继续编写代码,并将返回的代码作为下一段代码的一部分使用,那么如何将返回的值保存在寄存器或堆栈中?

基本上,您必须执行以下操作:

  • 使用
    pipe()
    系统调用进行fifo
  • fork()
    关闭子进程。在父进程中,这将返回您需要记住的子进程ID。在子对象中,该值返回零
  • 在父级中,关闭fifo的写入端,在子级中关闭读取端
  • 在子级中,使用
    dup2()
    将管道的写入端移动到文件描述符1。然后关闭管道的原始文件描述符
  • 在子对象中,
    execve
    要运行的程序。请注意,
    execve
    将当前进程替换为一个新映像,因此我不确定您对上一个程序的预期效果如何
  • 在父级中,从管道读取,直到遇到EOF(即
    read()
    返回零)。你读的是孩子的标准输出。将该输出存储在缓冲区中
  • 最后,使用
    wait()
    收集到孩子的退出状态。如果不这样做,死子进程将作为僵尸在进程表中徘徊,占用资源

  • 除了管道之外,您还可以
    open()
    将输出写入的文件。这样做的好处是,您可以使用
    fstat()
    了解进程写入了多少输出。但是,您需要实现类似于
    tmpfile
    函数的逻辑来创建和打开临时文件。

    基本上,您必须执行以下操作:

  • 使用
    pipe()
    系统调用进行fifo
  • fork()
    关闭子进程。在父进程中,这将返回您需要记住的子进程ID。在子对象中,该值返回零
  • 在父级中,关闭fifo的写入端,在子级中关闭读取端
  • 在子级中,使用
    dup2()
    将管道的写入端移动到文件描述符1。然后关闭管道的原始文件描述符
  • 在子对象中,
    execve
    要运行的程序。请注意,
    execve
    将当前进程替换为一个新映像,因此我不确定您对上一个程序的预期效果如何
  • 在父级中,从管道读取,直到遇到EOF(即
    read()
    返回零)。你读的是孩子的标准输出。将该输出存储在缓冲区中
  • 最后,使用
    wait()
    收集到孩子的退出状态。如果不这样做,死子进程将作为僵尸在进程表中徘徊,占用资源

  • 除了管道之外,您还可以
    open()
    将输出写入的文件。这样做的好处是,您可以使用
    fstat()
    了解进程写入了多少输出。但是,您需要实现类似于
    tmpfile
    函数的逻辑来创建和打开临时文件。

    除非失败,否则不会返回.execve的可能副本。除非失败,否则不会返回.execve的可能副本。