不一样的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【第四弹】的更多相关文章

  1. pwn学习日记Day19 《程序员的自我修养》读书笔记

    windows PE/COFF章总结 本章学习了windows下的可执行文件和目标文件格式PE/COFF.PE/COFF文件与ELF文件非常相似,它们都是基于段的结构的二进制文件格式.Windows下 ...

  2. pwn学习日记Day21 《程序员的自我修养》读书笔记

    Linux内核装载ELF过程 (1)bash进程调用fork()系统调用创建一个新的进程 (2)新的进程调用execve()系统调用执行指定的ELF文件,原先的bash进程继续返回等待刚才启动的新进程 ...

  3. pwn学习日记Day20 《程序员的自我修养》读书笔记

    可执行文件的装载与进程 覆盖装入和页映射是两种典型的动态装载方法 进程建立的三步 1.创建一个独立的虚拟地址空间 2.读取可执行文件头,并且建立虚拟空间与可执行文件的映射关系. 3.将CPU的指令寄存 ...

  4. 第八周读书笔记(人月神话X月亮与六便士)——到底什么才是一个程序员的自我修养?

    写了这么久的读书笔记,涉及到问题大多是一些如何把软件工程做好,如何把自己的职业生涯做好.但总感觉逻辑链上缺了一环,亦即:我们为什么要把软件工程做好,我们成为一名优秀的职业生涯的意义到底在于什么?我觉得 ...

  5. gcc ld 链接器相关知识,调试指令(程序员的自我修养----链接、装载与库)

    最近解决一个动态链接上的问题,因为以前从来没有接触过这方面的知识,所以恶补了一下,首先要了解gcc编译指令(makefile),ld链接器的选项(还有连接脚本section指定内存位置),熟悉查看连接 ...

  6. 程序员的自我修养(2)——计算机网络(转) good

    相关文章:程序员的自我修养——操作系统篇 几乎所有的计算机程序,都会牵涉到网络通信.因此,了解计算机基础网络知识,对每一个程序员来说都是异常重要的. 本文在介绍一些基础网络知识的同时,给出了一些高质量 ...

  7. 程序员的自我修养:高效使用Google解决问题

    如果票选近二十年最伟大的发明,我相信搜索引擎肯定会占据一个不容小觑的位置,它不单是一项发明,更是一项成就,最大程度消灭了信息的不平等.既然人人都可以接触到海量的信息,那么衡量信息财富多寡就只剩下技巧这 ...

  8. pwn学习日记Day18 《程序员的自我修养》读书笔记

    知识杂项 obj文件:当前源代码编译成二进制目标文件 exe文件:将.obj文件与库文件.lib等文件链接生成的可执行文件 一个现代编译器的主要工作流程如下: 源程序(source code)→ 预处 ...

  9. 《程序员的自我修养》读书笔记——系统调用、API

        系统调用 程序运行的时候,本身是没有权限访问多少系统资源的.系统资源有限,如果操作系统不进行控制,那么各个程序难免会产生冲突.线程操作系统都将可能产生冲突的系统资源保护起来,阻止程序直接访问. ...

  10. Java程序员的自我修养

    一.自我修养路线图 如图,这是笔者所走的路.且不论这路走的对不对,这个过程中行业环境会影响到你,大可不必钻牛角尖.附上这张图的目的是为了说,如果你想成为一个优秀的程序员,那么你一定要有规划.当然,别想 ...

随机推荐

  1. ubuntu18.04.3新装系统安装QT5.14.1和环境配置

    第一步:下载QT: http://download.qt.io/archive/qt/ 或者 https://mirrors.tuna.tsinghua.edu.cn/qt/archive/qt/ 下 ...

  2. MarkDown学习笔记1

    # MarkDown学习(一级标题)## 标题(二级)### 三级标题#### 四级标题## 字体*Hello,World!斜体***Hello,World!加粗*****Hello,World!加粗 ...

  3. Java8:LocalDate/ LocalDateTime与String、Date、TimeStamp的互转

    LocalDate与String.Date.TimeStamp的互转: LocalDateTime与String.Date.TimeStamp的互转: 结果如下: 附代码: public static ...

  4. SOC平台,网络安全管理平台

    SOC平台,网络安全管理平台 提供集中.统一.可视化的安全信息管理,通过实时采集各种安全信息,动态进行安全信息关联分析与风险评估,实现安全事件的快速跟踪.定位和应急响应. 从监控.审计.风险和运维四个 ...

  5. scala流程控制

    1.分支控制if-else 分支控制有三种:单分支.双分支.多分支: 1.1 单分支 (1).语法入下: if(条件表达式){ 执行代码块       //当条件表达式为true时,才会执行代码块内容 ...

  6. 解决方案-Jmeter在CLI模式下无法生成html报告文件

    出错场景: 在CLI模式下运行jmeter,将 jtl 结果转为 html 报告时,提示An error occurred: Data exporter "html" is una ...

  7. python脚本监控定时任务

    1.linux服务器中输入命令 crontab -l 查看当前系统的所有定时任务 2. 输入命令 crontab -e ,然后按"i"进行编辑(可新增.修改定时任务).具体定时任务 ...

  8. 昨天写了个ATM系统,学了点思想

    昨天上黑马的基础课终结,有个小测验是做个ATM系统 拿到之后我就明白该怎么做了 但是在后面遇到了瓶颈 就是 不知道怎么进入下级菜单 后面稍微看了一下他的做法,学会了 就继续做了,后面就做出来了 他这个 ...

  9. C语言——函数

    C 语言中的函数定义形式: return_type function_name( parameter list ) //函数头 { //函数体 body of the function } 返回类型: ...

  10. 视觉里程计--视觉slam7.1/相机运动估计视觉算法

    视觉里程计 本篇文章记录了少许阅读<视觉slam14讲>的阅读整理,不是特别全面,只是为了本次项目中特定任务搜查资料,时间比较紧,文章并没有全面涵盖所有知识点.日后若时间有空闲,将回来补充 ...