快速回忆
bash
vim my_shell.sh #如果修改shell名字,也需要修改c中对应的array和len宏定义
xxd -i my_shell.sh > my_shell.h
vim c_shell.c #拷贝本文的c_shell.c
gcc c_shell.c
./a.out
./a.out 1 2 3 #带参数执行,参数会被带到sub cmd中
# 修改脚本后需要全部重新来一遍,并且要删除掉默认路径的脚本,不然不会重新生成
注意的一点是,需要删掉脚本
背景
有些时候,用C语言想包括一个文件到运行态,因为C语言是静态程序,并且include的关键值只支持C格式,如何做到将某个文件存在于C语言中。
本文以一个C语言的例子,执行一个shell脚本,并且该脚本存在于C语言的bin文件中。
有什么用呢?
有些场景下不想提供脚本,只提供一个bin二进程程序。
方式
方式就是将脚本存储在C语言的数组中,作为内存动态加载,如果要执行,将该内存写入临时文件并且修改权限后执行。
先生成脚本的头文件
使用xxd命令,将二进制文件转化为C语言数组,然后C语言 include,然后动态的比如创建文件,然后执行文件。
xxd命令是用来制作一个十六进制dump的命令。比如:xxd -i 1.sh > shell_array.h
他会将文件按照十六进制生成一个C语言的头文件,并且头文件会用原来文件的名字创建数组(如果数字开头会添加__前缀,如果字母不会),并且把长度也标注出来
字母开头的文件:
可以看到 0x75是117,对应字符是u;最后一个字符是0xa, 表示\n
然后将该头文件包含
c_shell.c的文件
bash
#include "stdio.h"
#include "unistd.h"
#include "string.h" //strlen需要
#include "my_shell.h" //脚本的数组
#define SHELL_ARRAY_NAME my_shell_sh //这里是xxd -i生成文件中的数组名字
#define SHELL_ARRAY_LEN my_shell_sh_len //这里是xxd -i生成文件中的数组长度
#define SHELL_TMP_NAME "/tmp/my_shell.sh"
int del_shell_script() {
unlink(SHELL_TMP_NAME);
}
int gen_shell_script() {
printf("Will locate script at :%s\n", SHELL_TMP_NAME);
FILE *fp = fopen(SHELL_TMP_NAME, "wb");
if (!fp) {
printf("Failed open 1.log\n");
return -1;
}
if (fwrite(SHELL_ARRAY_NAME, 1, SHELL_ARRAY_LEN, fp) != SHELL_ARRAY_LEN){
printf("Failed to write %s\n", SHELL_TMP_NAME);
fclose(fp);
//unlink("1.log"); //删除文件
return 1;
}
fclose(fp);
实操
脚本创建:
bash
[root@localhost lab]# cat my_shell.sh
echo $@
uname -r
生成C的array:xxd -i my_shell.sh > my_shell.h
创建c_shell.c:如果不变化名字,可以直接使用前面的c_shell.c如果变化名字,需要修改这几个地方:
bash
#include "my_shell.h" //脚本的数组
#define SHELL_ARRAY_NAME my_shell_sh //这里是xxd -i生成文件中的数组名字
#define SHELL_ARRAY_LEN my_shell_sh_len //这里是xxd -i生成文件中的数组长度
#define SHELL_TMP_NAME "/tmp/my_shell.sh"
my_shell.h是根据xxd输出文件决定。
my_shell_sh和my_shell_sh_len是xxd的输入文件名字决定
SHELL_TMP_NAME是指定动态创建的地方。
编译新的bin文件和执行:gcc c_shell.c
如果要带参数执行,参数会被透传到脚本或者sub的bin代码中:
其他
这里除了shell,还可以是可执行程序。玩法有很多。可以用一个bin包含另外一个bin。
综述
本文这种方式有挺多挺好玩的模式,比如常见的一些工具,可以用它来把命令封装,比如需要输入的特殊参数,比如不想让别人看到的。甚至还可以做一些加解密的动作在里面。