《程序员的自我修养》学习笔记——不一样的hello world【第四弹】
不一样的hello world
Linux 的系统调用
通过glibc提供的库函数
glibc 是 Linux 下使用的开源的标准 C 库,它是 GNU 发布的 libc 库,即运行时库。glibc 为程序员提供丰富的 API,除了例如字符串处理、数学运算等用户态服务之外,最重要的是封装了操作系统提供的系统服务,即系统调用的封装。
使用syscall直接调用
该函数定义在 unistd.h 头文件中,函数原型如下: long int syscall (long int sysno, ...) 1. sysno 是系统调用号,每个系统调用都有唯一的系统调用号来标识。
2. ... 为剩余可变长的参数,为系统调用所带的参数。
3. 返回值 该函数返回值为特定系统调用的返回值,在系统调用成功之后你可以将该返回值转化为特定的类型,如果系统调用失败则返回 -1。
通过int指令陷入
用户态程序通过软中断指令`int 0x80` 来陷入内核态,参数的传递是通过寄存器,eax 传递的是系统调用号,ebx、ecx、edx、esi和edi 来依次传递最多五个参数,当系统调用返回时,返回值存放在 eax 中。
【系统调用号可在 /usr/include/asm/unistd_32.h(unistd_64.h)中查询 】
系统调用函数
write 调用
ssize_t write(int fd, const void *buf, size_t count);
write 调用的调用号为4,eax=4
fd 表示被写入的文件句柄,这里要向终端输出,文件句柄为1,ebx=1
buf 表示要写入的缓冲区地址,ecx=str 【hello world!字符串】
size 表示要写入的字节数,edx=13 【hello world!】
exit 调用
void exit(int status);
exit 调用的调用号为1。
status 表示进程退出码,如我们平时的main程序return的数值会返回给系统库,由系统库将该数值传递给exit系统调用。
有了上面的铺垫我们就可以做到,写一个不一样的hello world 程序,并且做到以下要求:
- 不调用任何系统库
- 不使用main函数【结束进程使用系统调用函数exit】
代码实现
GCC/GNU 编译器和 Clang/LLVM 编译器默认使用 AT&T/UNIX 汇编语法, GCC 可以通过加参数 -masm=intel 来使用 Intel 汇编语法,该参数并不适用于 Clang。
这里使用 Intel 语法 【AT&T/UNIX 语法 相应修改即可】
/* helloworld.c */
char * str="hello world\n";
void print(){
__asm(
"mov edx,13\n\t" //字符串长度
"mov ecx,str\n\t" //待显示字符串
"mov ebx,1\n\t" //文件描述符(stdout)
"mov eax,4\n\t" //write 系统调用号
"int 0x80\n\t" //软中断指令 进入系统调用
);
}
void end(){
__asm(
"mov ebx,0\n\t" //进程退出码
"mov eax,1\n\t" //exit 系统调用号
"int 0x80\n\t"
);
}
void run()
{
print();
end();
}
进行编译
# -masm=intel Intel 语法
# -fno-builtin 关闭gcc内置函数功能
# 生成可重定向文件
gcc helloworld.c -c -masm=intel -fno-builtin
# -static 让ld使用静态链接方式链接程序
# -e 程序入口
# 生成可执行文件
ld -static -e run -o helloworld helloworld.o


可以看到成功生成可执行文件!
.text 保存的是程序的指令,它是只读的。
.rodata 保存的是字符串“HelloWorld!\n”,它也是只读的。
.data 保存的是Sir全局变量,看上去它是可读写的,但我们并没有在程序中改写该变量,所以实际上它也是只读的。
.comment保存的是编译器和系统版本信息,这些信息也是只读的。由于.comment里面保存的数据并不关键,对程序的运行没有作用,所以可以丢弃。
.eh_frame 可以通过把代码写入.eh_frame中(覆盖其原来的内容)可以实现binary大小基本没有变化。若存在该段,我们能够进行改写并无影响。
【但这个段也极大增加了elf文件的大小】
鉴于这些段的属性如此相似,原则上讲,我们可以把它们合并到一个段里面,该段的属性是可执行、可读的,包含程序的数据和指令。为了达到这个目的,我们使用Id链接脚本来控制链接过程。
ld链接脚本
/* tinyhelloworld.lds */
ENTRY(run)
SECTIONS
{
. = SIZEOF_HEADERS; /* = 旁的空格很关键,不然也会报错 */
.tinytext : {*(.text) *(.rodata) *(.data)}
/DISCARD/ : {*(.comment) *(.eh_frame)}
}
# "."表示当前虚拟地址
# SIZEOF_HEADERS 输出文件头的大小
# tinytext: 新合并段名称
# /DISCARD/: 丢弃其中段
链接命令
ld -static -T tinyhelloworld.lds -o tinyhelloworld helloworld.o -s
# 注意:这里 -T -o 的顺序不当,会引起报错
# -s 去除符号

可以看到,除了重定向表外,就只有.tinytext段了。并且可执行程序的大小为576字节。
《程序员的自我修养》学习笔记——不一样的hello world【第四弹】的更多相关文章
- pwn学习日记Day19 《程序员的自我修养》读书笔记
windows PE/COFF章总结 本章学习了windows下的可执行文件和目标文件格式PE/COFF.PE/COFF文件与ELF文件非常相似,它们都是基于段的结构的二进制文件格式.Windows下 ...
- pwn学习日记Day21 《程序员的自我修养》读书笔记
Linux内核装载ELF过程 (1)bash进程调用fork()系统调用创建一个新的进程 (2)新的进程调用execve()系统调用执行指定的ELF文件,原先的bash进程继续返回等待刚才启动的新进程 ...
- pwn学习日记Day20 《程序员的自我修养》读书笔记
可执行文件的装载与进程 覆盖装入和页映射是两种典型的动态装载方法 进程建立的三步 1.创建一个独立的虚拟地址空间 2.读取可执行文件头,并且建立虚拟空间与可执行文件的映射关系. 3.将CPU的指令寄存 ...
- 第八周读书笔记(人月神话X月亮与六便士)——到底什么才是一个程序员的自我修养?
写了这么久的读书笔记,涉及到问题大多是一些如何把软件工程做好,如何把自己的职业生涯做好.但总感觉逻辑链上缺了一环,亦即:我们为什么要把软件工程做好,我们成为一名优秀的职业生涯的意义到底在于什么?我觉得 ...
- gcc ld 链接器相关知识,调试指令(程序员的自我修养----链接、装载与库)
最近解决一个动态链接上的问题,因为以前从来没有接触过这方面的知识,所以恶补了一下,首先要了解gcc编译指令(makefile),ld链接器的选项(还有连接脚本section指定内存位置),熟悉查看连接 ...
- 程序员的自我修养(2)——计算机网络(转) good
相关文章:程序员的自我修养——操作系统篇 几乎所有的计算机程序,都会牵涉到网络通信.因此,了解计算机基础网络知识,对每一个程序员来说都是异常重要的. 本文在介绍一些基础网络知识的同时,给出了一些高质量 ...
- 程序员的自我修养:高效使用Google解决问题
如果票选近二十年最伟大的发明,我相信搜索引擎肯定会占据一个不容小觑的位置,它不单是一项发明,更是一项成就,最大程度消灭了信息的不平等.既然人人都可以接触到海量的信息,那么衡量信息财富多寡就只剩下技巧这 ...
- pwn学习日记Day18 《程序员的自我修养》读书笔记
知识杂项 obj文件:当前源代码编译成二进制目标文件 exe文件:将.obj文件与库文件.lib等文件链接生成的可执行文件 一个现代编译器的主要工作流程如下: 源程序(source code)→ 预处 ...
- 《程序员的自我修养》读书笔记——系统调用、API
系统调用 程序运行的时候,本身是没有权限访问多少系统资源的.系统资源有限,如果操作系统不进行控制,那么各个程序难免会产生冲突.线程操作系统都将可能产生冲突的系统资源保护起来,阻止程序直接访问. ...
- Java程序员的自我修养
一.自我修养路线图 如图,这是笔者所走的路.且不论这路走的对不对,这个过程中行业环境会影响到你,大可不必钻牛角尖.附上这张图的目的是为了说,如果你想成为一个优秀的程序员,那么你一定要有规划.当然,别想 ...
随机推荐
- HCIP-进阶实验02-ISIS协议部署
进阶实验02-ISIS协议部署 1 实验需求 设备 接口 IP地址 备注 R1 G0/0/0 10.1.123.1/24 R2 G0/0/0 10.1.123.2/24 G0/0/1 10.1.24. ...
- 阿里云 rocketMq 延时消息
初始化消费者和生产者 生产者 设置rocketmq的accesskey 和secretkey 以及rocketmq的 binder server. 首先 编辑一个配置类,将关于配置rocketmq的东 ...
- 微信小程序 的 openid,appid,unionid
openid: 用户在同一个小程序下的唯一表示,即同一个用户在不同的小程序下的openid是不同的 appid 小程序唯一凭证,即 AppID,每个小程序都有自己的一个appid unionid 如果 ...
- 在Unity3D中开发的Sketch Shader
Pencil Sketch Shader 特点 此素描渲染风格的Shader是顶点片元Shader,由本人手动编写完成. 此素描渲染风格的Shader已经在移动设备真机上进行过测试,可以直接应用到您的 ...
- Epoll的使用例子
本篇文章在上一篇的基础上,使用 epoll 实现了一个事件监听和回调处理的模块.如何编写一个使用该模块的例子呢? 监测什么类型的fd,监测什么类型的事件,监测到事件以后需要做什么?后来可以看看如何将 ...
- 创建一个Openpyxl
from openpyxl import Workbook wb = Workbook(r'C:\Users\yjiang3\Desktop\23.xlsx') ws = wb.active from ...
- Git简介及使用
1.Git简介 GIT= 版本控制 + 备份服务器 我们称用来存放上传档案的地方就做Repository.用中文来说,有点像是档案仓库的意思. 不过,通常我们还是使用Repository这个名词.当有 ...
- software engineering homework 1
1. 回顾你过去将近3年的学习经历 当初你报考的时候,是真正喜欢计算机这个专业吗? 你现在后悔选择了这个专业吗? 你认为你现在最喜欢的领域是什么(可以是计算机的也可以是其它领域)? 答:一开始感觉编程 ...
- 创建sqlSession对象操作数据库
1.加载核心配置文件 //加载mybatis核心配置文件,获取SqlSessionFactory String resource = "mybatis-config.xml"; I ...
- 第五天 pycharms 安装使用
python全栈开发笔记第5天笔记pycharms使用 集成开发环境(IDE,Integratde Development Encironment ) VIM #linux下经典的文本编辑器 Emac ...