📜  从 strace 输出解码信息

📅  最后修改于: 2022-05-13 01:57:32.686000             🧑  作者: Mango

从 strace 输出解码信息

当一个程序被执行时,它会多次切换到用户模式内核模式。在用户模式下,进程对资源的访问受到限制,而在内核模式下,它可以访问特权硬件资源及其数据。进程使用系统调用从用户模式切换到内核模式。

Strace是一个分析进程系统调用活动的工具。它为我们提供了以下信息:

  • 访问的文件。
  • 执行期间使用的系统调用
  • 每个系统调用进程所花费的时间等。

当您无法访问源代码并且仅使用可执行二进制文件完成调试时,分析系统调用会很有帮助。这篇文章不是关于如何使用 Strace 工具,它更多是关于分析Strace工具的输出,因为在进程上执行Strace时,它会转储很多与系统调用相关的信息。乍一看,它看起来非常可怕,分析每个系统调用将是一项非常耗时的任务。此外,它可能不是必需的,因为大多数启动系统调用都是为了内务处理,并没有为调试增加太多价值。一旦了解了一个进程的系统调用流程,就可以轻松识别并删除内务系统调用,并专注于调试我们实际问题的重要系统调用。

程序:

C
// C program to print Hello World!
// filename: hello.c
#include 
  
// Driver Code
int main(int argc, char* argv[])
{
    // Print Hello World
    printf(" geeksforgeeks: hello world !! \n");
  
    return 0;
}


输出:

geeksforgeeks: hello world !!

使用以下命令编译上述程序:

$ gcc hello.c

使用以下命令从上面编译的程序中找到 Strace:

$ strace ./a.out

现在,上述程序的Strace输出为:

在开始分析系统调用之前,先简单说一下程序执行wrt系统调用:

  • “Hello World”程序将打开并将所有共享库的内存映射到进程的虚拟内存中。大多数系统调用都与此活动相关。
  • 设置对内存部分的正确访问。
  • 最后执行程序,它会写出消息“geeksforgeeks: hello world !!”进入流程的标准输出

解码 Strace 输出

现在将Strace输出分成有意义的块以便更好地理解:

  • 执行():

    当我们在 bash 控制台中运行./a.out可执行文件时,一个新的子进程被派生并运行execve()以加载新程序“a.out”

    它有3个参数:

    • 第一个参数是可执行文件名
    • 第二个参数是可执行文件的参数数组,其中第一个参数是可执行文件名称本身。由于没有给可执行文件提供参数,我们只能在参数列表中看到./a.out
    • 第三个参数是字符串环境变量。

    这里的返回值为0 ,表示成功。有关execve()系统调用的更多详细信息,请使用“man 2 execve”在 execve 的手册页上找到。

  • brk():此系统调用将数据段大小设置为指定地址。这里使用brk(NULL)来获取数据段的顶部地址,也就是堆起始地址。因此,使用 NULL 调用brk() 会返回堆起始地址,该地址稍后用于分配堆内存。

  • access():这个系统调用检查文件权限。它有2个参数:
    • 第一个参数是必须检查权限的文件名。
    • 第二个参数是模式,它指定可访问性检查。检查文件的读取、写入和可执行可访问性。这里F_OK用于存在检查, R_OK用于读取检查。
    • 如果返回值为-1 ,则表示检查的文件不存在。

  • ld.so.nohwcap:此文件的存在会禁用优化库的加载。在最新的发行版中,此文件不存在。 ld.so.preload文件包含要在程序之前加载的共享对象文件列表。

    • openat()打开一个文件/etc/ld.so.cache并返回文件描述符3/etc/ld.so.cache包含应搜索共享库的目录列表。
    • fstat()获取相同文件描述符的文件属性,如模式、大小、创建/修改时间戳等。第二个参数是读取的属性的详细信息。
    • mmap()使用文件大小127481,fstat()读取并将整个文件映射到进程的虚拟内存中,并返回映射的虚拟内存地址0x7ff58cf81000。
    • 成功映射后,使用close()系统调用关闭文件。
  • /etc/ld.so.cache:这包含应在其中搜索共享库的目录列表。

    这与第 3 行相同。

    上面的系统调用块是关于打开libc库并将其映射到进程的虚拟内存中。

    • openat()打开/lib/x86_64-linux-gnu/libc.so.6并返回文件描述符3 。文件描述符 3 进一步用于处理libc文件。
    • read()读取 832 字节的libc.so文件。第二个参数是读取832字节的数据,是ELF文件的头信息,可能用于ELF文件的校验。
    • fstat()获取libc文件属性。
    • mmap()将文件映射到虚拟内存中。
    • mprotect()更新内存区域的保护
    • close()释放文件描述符,因为文件已成功映射到进程虚拟内存中,并且不再需要通过文件描述符进行访问。
  • arch_prctl():它设置特定于体系结构的线程状态。这里将 FS 寄存器的64 位基址设置为地址0x7ff58cf804c0



  • mprotect():调用为不同的内存区域设置保护。 PROT_READ用于使内存区域可读。

  • munmap():调用取消映射文件/etc/ld.so.cache 。地址0x7ff58cf81000在第 7 行映射到 ld.so.cache。

  • fstat():这是在文件描述符1上完成的,以获取它的属性,因为 printf() 将使用 stdout 描述符写入数据。

    当一个进程启动时,它会打开 3 个默认文件:
    • stdin 的文件描述符 0。
    • stdout 的文件描述符 1。
    • stderr 的文件描述符 2。
  • brk():调用获取和设置数据段边界。
  • 这对应于printf()语句,它使用write() 系统调用将数据放在进程的标准输出上。
  • 程序以0(SUCCESS)退出。

以下是使用“-static”选项构建的“Hello World”程序的 Strace 输出:

可以看到静态构建的可执行文件没有调用open()mmap()close()等,这是为了映射共享库而完成的。现在我们对系统调用有了足够的了解,可以分析 Strace 工具输出并筛选出感兴趣的系统调用进行调试。对于任何系统调用细节,最好的地方是它的手册页。可以使用以下命令访问。

$ man 2