linux二进制分析

文章大部分内容参考自书籍:《linux二进制分析》〔美〕Ryan O'Neill 著  人民邮电出版社

因为本人对二进制ELF方面的内容比较感兴趣,所以想要系统的学习一下linux二进制文件相关的技术以及利用技巧,上面提到的书解决了我很多的疑惑,强烈安利~~

正文开始:

工具:
1、gdb
2、objdump:是优秀的分析简单的elf文件的工具,能快速的进行反编译;缺陷是需要依赖ELF节头,并且不会进行控制流分析,如果要反编译的文件没有节头,那么使用objdump的后果就是无法正确的反编译二进制文件中的代码,甚至都不能打开二进制文件:
    1、objdump -D //查看ELF文件所有节的数据或代码
    2、objdump -d //只查看ELF文件中的程序代码
    3、objdump -tT //查看所有符号
3、objcopy
4、strace
5、ltrace
6、ftrace
7、readelf:解析ELF文件

有用的设备和文件
1、/proc/<pid>/maps        //保存了一个进程镜像的布局,展现的内容包括可执行文件、共享库、堆、栈、VDSO等
2、/proc/kcore            //linux内核的动态核心文件
3、/boot/System.map        //包含了整个内核的所有符号
4、/proc/kallsyms            //和System.map类似,区别是可以动态更新,它包含了内核中绝大部分符号
5、/proc/iomem            //跟/proc/<pid>/maps相似,不过是系统内存相关的,
6、ECFS                //(extended core file snapshot)(ECFS,扩展核心文件快照),是一项特殊的核心转储技术,专门为进程镜像的高级取证分析所设计。

链接器相关环境指针
1、LD_PRELOAD            //该环境变量可以设置成一个指定库的路径,动态链接时可以比其他库有更高的优先级。这就允许预加载库中的函数和符号能够覆盖掉后续链接的库中的函数和符号。这在本质上允许你通过重定向共享库函数来进行运行时的修复(他可以绕过反调试,也可以用作用户级rootkit)
2、LD_SHOW_AUXV        //该环境变量能够通知程序加载器来展示程序运行时的辅助向量,辅助向量是放在程序栈(通过内核的ELF常规加载方式)上的信息,附带了传递给动态链接器的程序相关的特定信息,这些信息对于反编译和调试非常有用,例如要想获取进程镜像VDSO页的内存地址,就需要查询AT-SYSINFO
3、链接器脚本            //链接器脚本是由链接器解释的,ld链接器程序有其自己解释的一套语言,通过链接器脚本可以控制可执行文件的布局,这在有些时候相当重要

ELF文件组成:
一、ELF程序头
    定义:ELF程序头是对二进制文件中段的描述,是程序装载必须的一部分。段是在内核装载时被解析的,描述了磁盘上可执行文件的内存布局以及如何映射到内存中
    1、PT_LOAD        //描述的是可装载的段,也就是说,这种类型的段将被装载或者映射到内存中(eg:text段和data段)
    2、PT_DYNAMIC        //为动态段,是动态链接可执行文件所特有的,包含了动态链接器所必需的一些信息。在动态段中包含了一些标记值和指针,包括但不限于(1、共享库列表,2、GOT表,3重定位条目的相关信息。还包含了一些结构体,在这些结构体中存放着与动态链接相关的信息。)
    3、PT_NOTE        //可能保存了与特定供应商或者系统相关的附加信息。不过在可执行文件运行时是不需要这个段的,所以这个段成了很容易被病毒感染的一个地方
    4、PT_INTERP        //只将位置和大小信息存放在一个以null为终止符的字符串中,在gdb中经常可以看到有关于elf文件位置以及大小的字符串
    5、PT_PHDR        //保存了程序头表本身的位置和大小。phdr表保存了所有的phde对文件(以及内存镜像)中段的描述信息
二、ELF节头
    定义:节、不是段。段是程序执行的必要组成部分,在每个段中。会有代码或者数据被划分为不同的节。节头表是对这些节的位置和大小的描述,主要用于链接和调试,(所以说节头对于程序的执行来说并不是必需的),没有节头表,程序依然可以正常执行,也并不意味着节就不存在,只是没有办法通过节头来引用节,对于调试器或者反编译程序来说,只是可以参考的信息变少了而已(需要提醒一点,objdump还有gdb都是依赖节头定位到存储符号数据的节来获取符号信息。如果没有节头,gdb以及objdump几乎无用武之地),但是节头表被破坏或者删除,我们可以重构节头表甚至重构部分符号表,原理就是通过程序头以及保存符号表以及重定位入口信息的DT_TAG
    1、.text            //保存了程序代码指令的代码节
    2、.rodata        //保存了只读的数据,他在text段中!!!()因为他是只读的嘛,data段是可读可写的
    3、.plt            //过程链接表:包含了动态链接器调用从共享库导入函数所必需的相关代码,在text段中
    4、.data            //在data段中,保存了已初始化的全局变量等数据
    5、.bss            //在data段中,保存了未初始化的全局变量等数据,占用空间不超过四个字节,因为没存任何东西
    6、.got.plt        //在data段中,.got保存了全局偏移表,.got和.plt一起提供了对导入的共享库函数的访问入口,由动态链接器在运行时修改。如果攻击者获得了堆或者.bss漏洞的一个指针大小的写语句,就可以对该节任意修改
    7、.dynsym        //在text段中,保存了从共享库导入的动态符号信息
    8、.dynstr        //保存了动态符号字符串表,表中存放了一系列字符串,这些字符串代表了符号的名称,以空字符作为终止符
    9、.rel.*            //保存了重定位相关的信息,也就是字面上的“rel”
    10、.hash            //保存了一个用于查找符号的散列表
    11、.symtab        //保存了elfN_Sym类型的符号信息
    12、.strtab        //保存的是符号字符串表,表中的内容会被.symtab的elfN_Sym结构中的st_name条目引用
    13、.shstrtab        //保存节头字符串表,该表是一个以空字符终止的字符串的集合,字符串保存了每个节的节名,如.text、.data 等
    14、.ctors&.dtors        //.ctors(构造器)和.dtors(析构器)这两个节保存了指向构造函数和析构函数的函数指针(黑客或病毒制造者有时会利用构造函数属性实现一个函数,实现类似 PTRACE_TRACEME 这样的反调试功能,这样进程就会追踪自身,调试器就无法附加到这个进程上。通过这种方式,在程序进入 main()函数之前就会先执行反调试的代码)
    总结:text段包括:(.text、.rodata、.hash、..dynsym、.dynstr、.plt、.rel.got);
             data段包括:(.data、.dynamic、.got.plt、.bss)
    tips:    可以通过readelf -S查看程序节头
        可重定位文件无程序头,不过 Linux 中的可加载内核模块(LKM)是个例外,LKM 是 ET_REL 类型的文件,它会被直接加载进内核的内存中并自动进行重定位
三、ELF符号
    定义:符号是对某些类型的数据或者代码(如全局变量或函数)的符号引用。在大多数共享库和动态链接可执行文件中,存在两个符号表.dynsym和.symtab。.dynsym 保存了引用来自外部文件符号的全局符号,如 printf 这样的库函数,.dynsym 保存的符号是.symtab 所保存符号的子集,.symtab 中还保存了可执行文件的本地符号,如全局变量,或者代码中定义的本地函数等。
        那么既然.symtab 中保存了.dynsym 中所有的符号,那么为什么还需要两个符号表呢?(因为.dynsym是被标记为ALLOC的,会在运行时分配并装入内存,而.symtab不是在运行时必需的,不会被装载到内存中),.dynsym 保存的符号只能在运行时被解析,因此是运行时动态链接器所需要的唯一符号。.dynsym 符号表对于动态链接可执行文件的执行来说是必需的,而.symtab 符号表只是用来进行调试和链接的,有时候为了节省空间,会将.symtab 符号表从生产二进制文件中删掉。
    1、st_name        //保存了指向符号表中字符串表(位于.dynstr 或者.strtab)的偏移地址,偏移地址存放着符号的名称,如 printf。
    2、st_value        //存放符号的值(可能是地址或者位置偏移量)。
    3、st_size            //存放了一个符号的大小,如全局函数指针的大小,在一个 32 位系统中通常是 4 字节。
    4、st_other        //定义了符号的可见性。
    5、st_shndx        //每个符号表条目的定义都与某些节对应。st_shndx 变量保存了相关节头表的索引。    
    6、st_info            //指定符号类型及绑定属性
    符号类型:
    1、STT_NOTYPE        //符号类型未定义
    2、STT_FUNC        //表示该符号与函数或者其他可执行代码关联。
    3、STT_OBJECT        //表示该符号与数据目标文件关联。
    符号绑定:
    1、STB_LOCAL        //本地符号在目标文件之外是不可见的,目标文件包含了符号的定义,如一个声明为 static 的函数。
    2、STB_GLOBAL        //全局符号对于所有要合并的目标文件来说都是可见的。一个全局符号在一个文件中进行定义后,另外一个文件可以对这个符号进行引用。
    3、STB_WEAK        //与全局绑定类似,不过比 STB_GLOBAL 的优先级低。被标记为 STB_WEAK 的符号有可能会被同名的未被标记为STB_WEAK 的符号覆盖。
    所谓的删除符号表:就是保留.dynsym,丢弃.symtab,这样在IDA里面,函数所表示出来的函数名就是SUB_<address_of_function>的形式
四、ELF重定位
    定义:重定位就是将符号定义和符号引用进行连接的过程。可重定位文件需要包含描述如何修改节内容的相关信息,从而使得可执行文件和共享目标文件能够保存进程的程序镜像所需的正确信息。重定位条目就是我们上面说的相关信息。(说白了就是.o文件将里面的函数分配内存的过程,重定位实际上是一种给二进制文件打补丁的机制)
    基于二进制修补的重定位代码注入:重定位代码注入是黑客、病毒制造者或者任何想修改二进制文件中代码的人常用的一种技术。在二进制文件编译完成并链接到一个可执行文件之后,通过重定位代码技术可以重新链接二进制文件。这就意味着,可以将一个目标文件注入到可执行文件中,更改可执行文件的符号表来指向新注入的功能,并对注入的目标代码进行必要的重定位,那么注入的代码就变成了可执行文件的一部分。(就是将一个.o文件替换原有的函数或者导入表里面的东西,),我们可以用Eresi进行重定位代码注入入(也称 ER_REL 注入)
五、ELF动态链接
    辅助向量:通过系统调用 sys_execve()将程序加载到内存中时,对应的可执行文件会被映射到内存的地址空间,并为该进程的地址空间分配一个栈。这个栈会用特定的方式向动态链接器传递信息。这种特定的对信息的设置和安排即为辅助向量(auxv)。
    程序被加载进内存,辅助向量被填充好之后,控制权就交给了动态链接器。动态链接器会解析要链接到进程地址空间的用于共享库的符号和重定位。默认情况下,可执行文件会动态链接 GNU C 库 libc.so。ldd 命令能显示出一个给定的可执行文件所依赖的共享库列表。
    延迟绑定:通过PLT(过程链接表)和 GOT(全局偏移表)。PLT[0]相对于GOT的偏移为GOT[3],下面是 GOT 的 3 个偏移量。
            GOT[0]:存放了指向可执行文件动态段的地址,动态链接器利用该地址提取动态链接相关的信息。
 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值