linux中为什么脚本的第一行要以

云计算分享者 2024-10-23 16:43:01
0x00 无处不在的#!

在linux中,我们写一个脚本,基本的步骤是

1.第一行写#!/bin/bash

2.然后把所有的内容写好

3.使用chmod +x t.sh给脚本加上可执行权限

4.使用./t.sh来执行脚本.

但是大家有没有想过,为什么我们要在第一行的最前面加上#!,有很多文章都说这个是提醒Linux使用/bin/bash来执行这个脚本. 但是这整个过程是怎么回事呢?

0x01 使用lsof得到真正运行的elf文件

为了得到脚本真正运行的可执行文件,我们使用如下的脚本来测试

#cat main.sh#!/bin/bashecho "hello world in a shell "echo "pid=$$,ppid=$PPID"lsof -p $$

其中$$表示当前的进程的pid. lsof -p $$则用来输出当前进程打开的问题,其中txt类型表示可执行文件的路径.

可以看到这个脚本确实用的是bash

lsof总会输出文件的绝对路径,这儿/bin是到/usr/bin的软连接.

0x02 exec对shebang(#!)的处理

Linux中运行一个脚本,走的是fork+exec+wait的3个系统调用. 对于#!的处理,实际就在exec这个系统调用中.exec系统调用的函数原型如下

#include <unistd.h> int execve(const char *filename, char *const argv[], char *const envp[]);

exec会读入filename指定文件,这儿就是main.sh,然后不是马上当成一个elf文件来load并执行. 而是发现前面两个字节的内容是#!,exec就知道现在不是要把这个文件直接当做一个elf文件来执行,而是继续解析这一行,把#!后面的路径指定的文件/bin/bash作为可执行文件来执行.而把./main.sh作为其参数. 类似于/bin/bash ./main.sh. 当然,其中的argv[0]会特殊处理,设置其值为./main.sh而不是/bin/bash.

看了上面的描述,我们其实可以想到,把这个概念延展一下,如果我可以自定义exec的处理逻辑,让其真正调用的ELF可执行文件是可配置的,是不是就相当强大了.这个就是binfmt_misc了.

0x03 shebang的进阶,binfmt_misc

首先,我们看如下文件main.arm,其为一个elf文件,但是不是x86_64的,而是ARM的.

#file main.armmain.arm: ELF 32-bit LSB executable, ARM, EABI5 version 1 (SYSV), statically linked, for GNU/Linux 3.2.0, BuildID[sha1]=21bae788e592e087938e90d9c2f6c7b719845922, not stripped

我自己的系统是x86_64的,按道理说,是无法直接运行的.

[root@fc.cxy.com ~/toutiao/shebang] 0#uname -aLinux fc.cxy.com 4.20.8-100.fc28.x86_64 #1 SMP Wed Feb 13 13:09:13 UTC 2019 x86_64 x86_64 x86_64 GNU/Linux

但是我却可以运行它.是不是很神奇.

从上面的输出其实已经可以看到,真正运行的elf文件是/usr/bin/qemu-arm,等价运行了这个命令

/usr/bin/qemu-arm /root/toutiao/shebang/main.arm

/usr/bin/qemu-arm是x86_64下的可执行文件,专门用来执行arm版本可执行文件的.

#file /usr/bin/qemu-arm/usr/bin/qemu-arm: ELF 64-bit LSB shared object, x86-64, version 1 (GNU/Linux), dynamically linked, interpreter /lib64/l, for GNU/Linux 3.2.0, BuildID[sha1]=1b9861278f96fd5df6c02cb587682b83430e3969, stripped, too many notes (256)

那Linux是怎么知道这个mian.arm文件需要用/usr/bin/qemu-arm来执行呢?

binfmt_misc是一个特殊的文件系统,其一般挂载在/proc/sys/fs/binfmt_misc中

#cat /proc/mounts |grep ^binfmt_miscbinfmt_misc /proc/sys/fs/binfmt_misc binfmt_misc rw,relatime 0 0

目录中每个文件的内容是对一类文件的配置

我们看下qemu-arm的内容.

可以看到,interpreter指定了当匹配上文件之后执行的命令. offset,magic,mask指定了对文件的匹配. 我们用xxd得到main.arm文件的头,发现和qemu-arm中的配置匹配,所以main.arm会被/usr/bin/qemu-arm执行.

上面的main.arm文件使用的源代码如下

root@42aab49cbbc5:~# cat main.c#include <stdio.h>#include <unistd.h>#include <stdlib.h>#include <sys/types.h>int pid,ppid;int main(){ char buf[1024]; int n; printf("hello world in arm elf file\n"); pid=getpid(); ppid=getppid(); printf("pid=%d,ppid=%d\n",pid,ppid); sprintf(buf,"lsof -p %d",pid); system(buf); return 0;}

使用如下的命令编译得到,也就是用的交叉编译器arm-linx-gcc编译.注意这儿需要加上-static选项,确保静态编译.

arm-linux-gnueabi-gcc -static main.c -o main.arm

这儿大家可以想想为什么要静态编译? 欢迎留言讨论.

0 阅读:0

云计算分享者

简介:感谢大家的关注