0x01 gcc

gcc工具readelf

解析elf文件结构

gcc参数

  • -c 编译和汇编,但不链接

    ex:gcc -c main.c 输出main.o文件,.o文件不可执行
    image.png
  • -o <file> 对.o进行链接

    ex:gcc -o main main.o 将main.o链接为可执行文件,输出main
    image.png
  • -g 如果想用调试器执行一个可执行文件, 在用gcc编译时必须加上-g选项,加上-g选项以后,gcc在编译时会做以下额外的操作:

    1. 创建符号表,符号表包含了程序中使用的变量名称的列表。
    2. 关闭所有的优化机制,以便程序执行过程中严格按照原来的C代码进行
  • -w 关闭编译时的警告,也就是编译后不显示任何warning,因为有时在编译之后编译器会显示一些例如数据转换之类的警告,这些警告是我们平时可以忽略的。
  • -Wall:开启所有警告(可以理解为warinig all),使用它能够使GCC产生尽可能多的警告信息。(非常推荐使用该选项)
  • gcc -S test.c 生成汇编文件
  • ar rcs libtest.a test.o 生成静态链接库文件
  • gcc -o test test.o -L. -ltest 链接静态库文件和.o文件生成可执行文件
    gcc -o test test.o -L/your/library/path -l:libmylib.a

    注意上面的说明中红框标注的内容,如果-l:filename格式指定一个文件名,连接程序直接去找这个文件名了,不会再像使用-lname时将name扩展成lib<name>.a格式的文件名.
    所以使用 -l:libpng.a这样的形式来指定连接库,就指定了静态连接png库。
    当然如果库的位置不在gcc默认搜索路径中,要用-L参数另外指定搜索库的路径,否则连接程序不知道该从哪里找到filename
  • 编译代码到共享库中,并使用操作系统的预加载功能。当执行/usr/bin下的被测应用时,如果系统库和我们创建的库中有相同的函数,会使用我们库中的函数。

    gcc -shared -fPIC -o test.so test.c 
    LD_PRELOAD=./test.so /usr/bin/target  
  • --cc=clang --extra-cflags='-O2 -g3 -fsanitize=address -fno-omit-frame-pointer -Wno-error' --extra-ldflags='-O2 -g3 -fsanitize=address -fno-omit-frame-pointer -Wno-error' --enable-debug --prefix=/home/cyber/VulnResearch/ffmpeg/clean/out_2
  • 流程示例
    1.预处理
    gcc -E test.c < test.i
    2.编译,生成汇编
    gcc -S test.i -o test.s
    3.汇编,生成机器码
    gcc -c test.s -o test.o
    4.链接,生成执行程序
    gcc test.o -o test
    每行汇编对应一行机器码
    例子:
    gcc -g -c test.c
    objdump -d -M intel -S test.o

gcc之addressSanitizer

https://www.jianshu.com/p/3a2df9b7c353
gcc 4.8以上支持addressSanitizer,其能检测的错误类型
image.png
用-fsanitize=address选项编译和链接你的程序;
用-fno-omit-frame-pointer编译,以在错误消息中添加更好的堆栈跟踪。
增加-O1以获得更好的性能(最基础的优化,分为1,2,3级)

简单源码示例:

#include <stdlib.h>
char leaktest(){
char* x =(char*)malloc(10*sizeof(char*));
return x[5];
}


int main(){
leaktest();
return 0;
}

编译:
gcc -fsanitize=address -fno-omit-frame-pointer -O1 -g leaktest.c -o leaktest

makefile

1、默认情况下,make 会以第一条规则作为其“终极目标”。
2、make在执行命令时会检测依赖文件的时间戳:
若依赖文件不存在或者依赖文件的时间戳比目标文件新,则执行依赖文件对应的命令。
若依赖文件的时间戳比目标文件老,则忽略依赖文件对应的命令。
3、make目标文件的依赖文件是按照从左到右的顺序生成的

1、makefile文件基本书写规则

TARGET... : PREREQUISITES...
COMMAND

TARGET:规则目标,可以为文件名或动作名
PREREQUISITES:规则依赖
COMMAND:命令行,必须以[TAB]开始,由shell执行

ex:

  1 #this is a makefile example
  2 
  3 main:main.o
  4     gcc -o main main.o
  5 
  6 main.o:main.c
  7     gcc -c main.c

使用:

image.png

2、变量使用

${变量名}
$(变量名)
$单字符变量名,Makefile有三个非常有用的变量。分别是$@(目标文件),$^(所有的依赖文件),$<(第一个依赖文件)
参考:https://www.cnblogs.com/baiduboy/p/6849587.html

ex:

# 变量定义
objects = program.o foo.o utils.o

program : $(objects)          #在依赖中引用变量
    gcc -o program ${objects} #在命令中引用变量

$(objects) : defs.h           #在目标中引用变量

3、变量的分类和赋值
根据变量定义时所使用的赋值操作符的不同,可以将变量分成两种类型(或者说是两种风格):

递归展开式变量直接展开式变量

使用赋值操作符=+=?=定义的变量都是递归展开式变量,使用赋值操作符 :=定义的变量为直接展开式变量

两种变量类型的的最根本区别在于:变量值的求值时机,递归式变量的求值时机在于变量被引用时,直接展开式的求值时机在于变量被定义时

4、makefile选项

  • CFLAGS: 指定头文件(.h文件)的路径,如:CFLAGS=-I /usr/include -I /path/include。同样地,安装一个包时会在安装路径下建立一个include目录,当安装过程中出现问题时,试着把以前安装的包的include目录加入到该变量中来。
  • LDFLAGS:gcc 等编译器会用到的一些优化参数,也可以在里面指定库文件的位置。用法:LDFLAGS=-static -L /usr/lib -L /path/to/your/lib。每安装一个包都几乎一定的会在安装目录里建立一个lib目录。如果明明安装了某个包,而安装另一个包时,它愣是说找不到,可以抒那个包的lib路径加入的LDFALGS中试一下。
  • LIBS:告诉链接器要链接哪些库文件,如LIBS = -lpthread -liconv

5、make参数

1、make -j4 用4核同时编译,默认1核,可以不用加这个...
2、make -n/--just-print/--dry-run/--recon 只检查Makefile的命令,输出组合后的结果,不执行

cmake

你或许听过好几种 Make 工具,例如 GNU Make ,QT 的 qmake ,微软的 MS nmake,BSD Make(pmake),Makepp,等等。这些 Make 工具遵循着不同的规范和标准,所执行的 Makefile 格式也千差万别。这样就带来了一个严峻的问题:如果软件想跨平台,必须要保证能够在不同平台编译。而如果使用上面的 Make 工具,就得为每一种标准写一次 Makefile ,这将是一件让人抓狂的工作。
CMake就是针对上面问题所设计的工具:它首先允许开发者编写一种平台无关的 CMakeList.txt 文件来定制整个编译流程,然后再根据目标用户的平台进一步生成所需的本地化 Makefile 和工程文件,如 Unix 的 Makefile 或 Windows 的 Visual Studio 工程。从而做到“Write once, run everywhere”。显然,CMake 是一个比上述几种 make 更高级的编译配置工具。一些使用 CMake 作为项目架构系统的知名开源项目有 VTKITKKDEOpenCVOSG 等 [[1]](https://www.hahack.com/codes/cmake/#fn1)。

在 linux 平台下使用 CMake 生成 Makefile 并编译的流程如下:

  1. 编写 CMake 配置文件 CMakeLists.txt 。
  2. 执行命令 cmake PATH 或者 ccmake PATH 生成 Makefile ,ccmake 和 cmake 的区别在于前者提供了一个交互式的界面。。其中, PATH 是 CMakeLists.txt 所在的目录。
  3. 使用 make 命令进行编译。

    mkdir bld
    cd bld
    cmake .. (CMakeLists.txt在上一级目录)
    make

0x02 clang

clang参数

clang使用libfuzzer进行fuzz所需参数
-fsanitize-coverage=trace-pc-guard(编译器在每条边界插入代码,每条边界的guard都不同。), trace-cmp(编译器将在比较指令和switch语句周围插入额外的工具),trace-gep(LLVM GEP指令(捕获数组索引)),trace-div(编译器将检测整型除法指令(以捕获除法的正确参数))

参考文档:https://www.anquanke.com/post/id/181936

0x03 实际场景

1、汇编代码如何生成可执行文件

生成可执行文件的过程通常需要以下几个步骤:
编写汇编代码文件,保存为后缀名为.asm的文件。
使用汇编器(assembler)将汇编代码文件转换成目标文件(object file),一般以.o为后缀名。
使用链接器(linker)将目标文件和所需的库文件链接起来,生成可执行文件。
具体操作可以根据使用的汇编器和链接器不同而有所不同。例如,使用GNU汇编器(Gas)和链接器(ld)可以通过以下命令生成可执行文件:

as -o your_file.o your_file.asm
ld -o your_executable your_file.o

2、如何查看汇编指定的机器码
要查看汇编指令的机器码,可以使用汇编编译器将汇编代码编译成目标文件,然后使用目标文件的反汇编工具来查看机器码。

  • objdump:这是一个常用的 GNU 工具,可以用来查看目标文件和可执行文件的反汇编代码。例如,要查看一个名为 test 的可执行文件的反汇编代码,可以在终端中运行以下命令:
    objdump -d test
    这将显示 test 可执行文件的所有反汇编代码,包括每个汇编指令的机器码。

3、分两步才可以编译,为什么?
gcc -c -I/home/dmdba/dmdbms/include odbc_dml.c -o odbc_dml.o
gcc -o odbc_dml odbc_dml.o -L/home/dmdba/dmdbms/bin -ldodbc

合成一步报错.. gcc -I/home/dmdba/dmdbms/include -L/home/dmdba/dmdbms/bin -ldodbc odbc_dml.c -o odbc_dml.o


白风之下
10 声望3 粉丝

« 上一篇
fuzz模糊测试
下一篇 »
初探Nginx

引用和评论

0 条评论