基础技术-ELF系列(3)-libelf使用

2024-06-02 04:52

本文主要是介绍基础技术-ELF系列(3)-libelf使用,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

成就更好的自己

本篇是基础技术系列中ELF相关技术的第三篇,也是计划中的最后一篇(后续遇到问题可能还会有后续)。本文将会以上一篇文章中提到的实际问题写一段Demo为例,着重讲解一下libelf库的基本使用。

没有看过之前文章的朋友请回顾一下之前的文章:

基础技术-ELF系列2-ELF文件进阶与libelf库-CSDN博客


目录

 

Demo的基本目的和思路

Demo实现与逐行分析


Demo的基本目的和思路

先回顾一下上一篇中的问题:

需要在不进行编译的条件下,对比几个库之间是否存在符号重复。

所谓符号,常见情况下只有源文件中的函数在编译之后能够形成符号,而变量和常量会转变为地址,没有符号这一概念。因此主要分析ELF文件中存在的函数符号即可。

在先前的文章中,我们大概了解了构成ELF文件的最小单位可以以节进行划分。因此我们的思路大概是这样的:

  1. 解析ELF文件的基本信息
  2. 找到哪些节会存放函数的符号
  3. 找到并解析这些节的节头信息
  4. 根据这些节头信息对这些节进行数据解析
  5. 得到描述符号单位的结构
  6. 判断这个符号是库里定义的实际符号还是引用的外部符号
  7. 对实际符号进行打印显示,并作下一步处理

Demo实现与逐行分析

#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <libelf.h>
#include <gelf.h>
#include <dlfcn.h>#include "log.h"int Find_And_Pirnt_Func_Sym(int fd)
{int i = 0;int sym_count = 0;int shdr_index = 0;Elf* elf = NULL;GElf_Ehdr ehdr = {0};GElf_Shdr shdr = {0};GElf_Sym sym = {0};Elf_Scn* scn = {0};Elf_Data* data = {0};//对打开的ELF文件进行初始解析拿到ELF结构句柄elf = elf_begin(fd, ELF_C_READ, NULL);CHECK_NULL(elf, {LOG_ERROR("Can not get elf handle");goto end;});//判断该文件是否为ELF格式CHECK_TRUE(elf_kind(elf) != ELF_K_ELF, {LOG_ERROR("File is not a elf obj");goto end;});//获取并解析ELF文件头结构CHECK_NULL(gelf_getehdr(elf, &ehdr), LOG_ERROR("Can not get elf head");goto end;);LOG_INFO("Get Elf Head Success!");//这里是个循环,需要会从第2个节头开始遍历(因为协议规定第1个节头是空节头),依次获取每个节头的句柄for(shdr_index = 1; (scn = elf_nextscn(elf, scn)) != NULL; shdr_index++){//通过节头句柄获取并解析节头结构CHECK_NULL(gelf_getshdr(scn, &shdr), LOG_ERROR("Can not get section[%d] head", shdr_index);continue;);//判断该节是否为包含函数符号的节,不是则continue到下一个节头CHECK_FAILED(shdr.sh_type == SHT_DYNSYM || shdr.sh_type == SHT_SYMTAB, LOG_INFO("Section[%d:%s] \r\t\t\t\t\t\t\t Type Is %08x, Is Not Function Section!", shdr_index, elf_strptr(elf, ehdr.e_shstrndx, shdr.sh_name), shdr.sh_type);continue;);LOG_INFO("Section[%d : %s] \r\t\t\t\t\t\t\t Type Is %08x, Is Function Section!", shdr_index, elf_strptr(elf, ehdr.e_shstrndx, shdr.sh_name), shdr.sh_type);//获取并解析该节头对应节的数据信息CHECK_NULL((data = elf_getdata(scn, NULL)), LOG_ERROR("Can not get section[%d] data", shdr_index);continue;);//通过节头信息中的节总长度和节中每个成员大小,得到节成员的数量sym_count = shdr.sh_size / shdr.sh_entsize;LOG_INFO("This Sction Have %d Sym", sym_count);//遍历节成员for(i = 0; i < sym_count; i++){//根据成员索引获取每一个符号描述信息CHECK_NULL(gelf_getsym(data, i, &sym), {LOG_ERROR("Can not get sym[%d] head", i);continue;});//打印这个符号的基本信息 名称 对应数据内容存放地址 符号标志 符号数据内容长度 其他LOG_INFO("Sym[%d : %s]: \r\t\t\t\t\t\t\t Addr:0x%08lx Info:%02x Size:%08lx Other:%02x",i, elf_strptr(elf, shdr.sh_link, sym.st_name), sym.st_value, sym.st_info, sym.st_size, sym.st_other);}}end://释放ELF结构句柄CHECK_NONULL(elf, elf_end(elf););return 0;
}int main(void)
{int fd = 0;//选择使用的ELF文件解析版本(该步骤为必须步骤,否则begin的时候会报错)CHECK_TRUE(elf_version(EV_CURRENT) == EV_NONE, {LOG_ERROR("elf_version error!");return 0;});//打开ELF文件fd = open("main.o", O_RDONLY, 0);CHECK_TRUE(fd < 0, {LOG_ERROR("can not open!");return 0;});//查找并打印符号业务Find_And_Pirnt_Func_Sym(fd);//关闭文件close(fd);return 0;
}

其他注意事项:

  1. elf_version(EV_CURRENT),该函数为配置程序接下来使用的ELF格式版本,必须进行指定,否则程序不知道以什么版本解析ELF文件;
  2. gelf_getehdr(elf, &ehdr),该函数执行成功后即可获得ELF文件头的信息,里面的结构成员就是readelf -h读出的内容;
  3. elf_nextscn(elf, scn),该函数的意义在于获取下一节头的句柄,这个下一节头就是以入参scn为参考的下一节头,若函数最后一个入参为空,则永远获取的是第2个节的节头;之所以不是第一个,是因为第1个节头规定为空节头,获取的节头句柄通过返回值返回;
  4. gelf_getshdr(scn, &shdr),该函数通过节头句柄获取并解析节头信息,里面的结构就是readelf -S读出的单个节头的内容;
  5. elf_getdata(scn, NULL),上面获取的这是节头信息,这个节的实际数据内容是通过这个函数进行解析的,这个函数或者这个节内容信息的句柄;
  6. elf_strptr(elf, shdr.sh_link, sym.st_name),这个函数是库中获取字符串的函数,一般有两个用处,一是获取这个节名,二是获取符号名;函数的第二个入参代表存放这些符号的节,第三个入参是在这个节中作的偏移,对应的字符串就在这个偏移上存着。存放节名的节索引一般在ELF头结构中的e_shstrndx成员中存放;存放符号名的节索引一般在该符号所属节的节头结构中的sh_link中存放;(这些内容在上一篇博客中说的很明白)
  7. 打印的符号中有很多是引用的外部符号,不是本函数中定义的函数,若只想查看本库中定义的符号,如何进行区分?可用过符号描述结构中的st_size成员进行区分,该成员存放符号对应程序数据的长度,若该长度为0说明该符号在本库中没有定义,是外部符号(比如print,malloc等)
  8. 若该ELF文件经过某些工具裁剪(例如objcopy),把节头信息或者符号信息给裁掉了,那就不能通过上述方法进行解析,该文件同时失去链接功能。这种操作一般只会对可执行文件这么搞,这样不会影响程序运行,而且可以大大加强程序的反编译难度,增加安全性。

对以上面这段代码为源码编出来的.o执行这段程序,即可得到如下执行结果:

[INFO]: Get Elf Head Success!
[INFO]: Section[1:.text]                                 Type Is 00000001, Is Not Function Section!
[INFO]: Section[2:.rela.text]                            Type Is 00000004, Is Not Function Section!
[INFO]: Section[3:.data]                                 Type Is 00000001, Is Not Function Section!
[INFO]: Section[4:.bss]                                  Type Is 00000008, Is Not Function Section!
[INFO]: Section[5:.rodata]                               Type Is 00000001, Is Not Function Section!
[INFO]: Section[6:.comment]                              Type Is 00000001, Is Not Function Section!
[INFO]: Section[7:.note.GNU-stack]                       Type Is 00000001, Is Not Function Section!
[INFO]: Section[8:.note.gnu.property]                    Type Is 00000007, Is Not Function Section!
[INFO]: Section[9:.eh_frame]                             Type Is 00000001, Is Not Function Section!
[INFO]: Section[10:.rela.eh_frame]                       Type Is 00000004, Is Not Function Section!
[INFO]: Section[11 : .symtab]                            Type Is 00000002, Is Function Section!
[INFO]: This Sction Have 31 Sym
[INFO]: Sym[0 : ]:                                       Addr:0x00000000 Info:00 Size:00000000 Other:00
[INFO]: Sym[1 : main.c]:                                 Addr:0x00000000 Info:04 Size:00000000 Other:00
[INFO]: Sym[2 : ]:                                       Addr:0x00000000 Info:03 Size:00000000 Other:00
[INFO]: Sym[3 : ]:                                       Addr:0x00000000 Info:03 Size:00000000 Other:00
[INFO]: Sym[4 : ]:                                       Addr:0x00000000 Info:03 Size:00000000 Other:00
[INFO]: Sym[5 : ]:                                       Addr:0x00000000 Info:03 Size:00000000 Other:00
[INFO]: Sym[6 : __func__.4318]:                          Addr:0x00000680 Info:01 Size:00000018 Other:00
[INFO]: Sym[7 : __func__.4332]:                          Addr:0x00000698 Info:01 Size:00000005 Other:00
[INFO]: Sym[8 : ]:                                       Addr:0x00000000 Info:03 Size:00000000 Other:00
[INFO]: Sym[9 : ]:                                       Addr:0x00000000 Info:03 Size:00000000 Other:00
[INFO]: Sym[10 : ]:                                      Addr:0x00000000 Info:03 Size:00000000 Other:00
[INFO]: Sym[11 : ]:                                      Addr:0x00000000 Info:03 Size:00000000 Other:00
[INFO]: Sym[12 : Find_And_Pirnt_Func_Sym]:               Addr:0x00000000 Info:12 Size:0000070f Other:00
[INFO]: Sym[13 : _GLOBAL_OFFSET_TABLE_]:                 Addr:0x00000000 Info:10 Size:00000000 Other:00
[INFO]: Sym[14 : elf_begin]:                             Addr:0x00000000 Info:10 Size:00000000 Other:00
[INFO]: Sym[15 : log_level]:                             Addr:0x00000000 Info:10 Size:00000000 Other:00
[INFO]: Sym[16 : printf]:                                Addr:0x00000000 Info:10 Size:00000000 Other:00
[INFO]: Sym[17 : elf_kind]:                              Addr:0x00000000 Info:10 Size:00000000 Other:00
[INFO]: Sym[18 : gelf_getehdr]:                          Addr:0x00000000 Info:10 Size:00000000 Other:00
[INFO]: Sym[19 : puts]:                                  Addr:0x00000000 Info:10 Size:00000000 Other:00
[INFO]: Sym[20 : gelf_getshdr]:                          Addr:0x00000000 Info:10 Size:00000000 Other:00
[INFO]: Sym[21 : elf_strptr]:                            Addr:0x00000000 Info:10 Size:00000000 Other:00
[INFO]: Sym[22 : elf_getdata]:                           Addr:0x00000000 Info:10 Size:00000000 Other:00
[INFO]: Sym[23 : gelf_getsym]:                           Addr:0x00000000 Info:10 Size:00000000 Other:00
[INFO]: Sym[24 : elf_nextscn]:                           Addr:0x00000000 Info:10 Size:00000000 Other:00
[INFO]: Sym[25 : elf_end]:                               Addr:0x00000000 Info:10 Size:00000000 Other:00
[INFO]: Sym[26 : __stack_chk_fail]:                      Addr:0x00000000 Info:10 Size:00000000 Other:00
[INFO]: Sym[27 : main]:                                  Addr:0x0000070f Info:12 Size:0000013b Other:00
[INFO]: Sym[28 : elf_version]:                           Addr:0x00000000 Info:10 Size:00000000 Other:00
[INFO]: Sym[29 : open]:                                  Addr:0x00000000 Info:10 Size:00000000 Other:00
[INFO]: Sym[30 : close]:                                 Addr:0x00000000 Info:10 Size:00000000 Other:00
[INFO]: Section[12:.strtab]                              Type Is 00000003, Is Not Function Section!
[INFO]: Section[13:.shstrtab]                            Type Is 00000003, Is Not Function Section!

这篇关于基础技术-ELF系列(3)-libelf使用的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1023077

相关文章

Spring Security 从入门到进阶系列教程

Spring Security 入门系列 《保护 Web 应用的安全》 《Spring-Security-入门(一):登录与退出》 《Spring-Security-入门(二):基于数据库验证》 《Spring-Security-入门(三):密码加密》 《Spring-Security-入门(四):自定义-Filter》 《Spring-Security-入门(五):在 Sprin

中文分词jieba库的使用与实景应用(一)

知识星球:https://articles.zsxq.com/id_fxvgc803qmr2.html 目录 一.定义: 精确模式(默认模式): 全模式: 搜索引擎模式: paddle 模式(基于深度学习的分词模式): 二 自定义词典 三.文本解析   调整词出现的频率 四. 关键词提取 A. 基于TF-IDF算法的关键词提取 B. 基于TextRank算法的关键词提取

使用SecondaryNameNode恢复NameNode的数据

1)需求: NameNode进程挂了并且存储的数据也丢失了,如何恢复NameNode 此种方式恢复的数据可能存在小部分数据的丢失。 2)故障模拟 (1)kill -9 NameNode进程 [lytfly@hadoop102 current]$ kill -9 19886 (2)删除NameNode存储的数据(/opt/module/hadoop-3.1.4/data/tmp/dfs/na

Hadoop数据压缩使用介绍

一、压缩原则 (1)运算密集型的Job,少用压缩 (2)IO密集型的Job,多用压缩 二、压缩算法比较 三、压缩位置选择 四、压缩参数配置 1)为了支持多种压缩/解压缩算法,Hadoop引入了编码/解码器 2)要在Hadoop中启用压缩,可以配置如下参数

Makefile简明使用教程

文章目录 规则makefile文件的基本语法:加在命令前的特殊符号:.PHONY伪目标: Makefilev1 直观写法v2 加上中间过程v3 伪目标v4 变量 make 选项-f-n-C Make 是一种流行的构建工具,常用于将源代码转换成可执行文件或者其他形式的输出文件(如库文件、文档等)。Make 可以自动化地执行编译、链接等一系列操作。 规则 makefile文件

使用opencv优化图片(画面变清晰)

文章目录 需求影响照片清晰度的因素 实现降噪测试代码 锐化空间锐化Unsharp Masking频率域锐化对比测试 对比度增强常用算法对比测试 需求 对图像进行优化,使其看起来更清晰,同时保持尺寸不变,通常涉及到图像处理技术如锐化、降噪、对比度增强等 影响照片清晰度的因素 影响照片清晰度的因素有很多,主要可以从以下几个方面来分析 1. 拍摄设备 相机传感器:相机传

【专题】2024飞行汽车技术全景报告合集PDF分享(附原数据表)

原文链接: https://tecdat.cn/?p=37628 6月16日,小鹏汇天旅航者X2在北京大兴国际机场临空经济区完成首飞,这也是小鹏汇天的产品在京津冀地区进行的首次飞行。小鹏汇天方面还表示,公司准备量产,并计划今年四季度开启预售小鹏汇天分体式飞行汽车,探索分体式飞行汽车城际通勤。阅读原文,获取专题报告合集全文,解锁文末271份飞行汽车相关行业研究报告。 据悉,业内人士对飞行汽车行业

pdfmake生成pdf的使用

实际项目中有时会有根据填写的表单数据或者其他格式的数据,将数据自动填充到pdf文件中根据固定模板生成pdf文件的需求 文章目录 利用pdfmake生成pdf文件1.下载安装pdfmake第三方包2.封装生成pdf文件的共用配置3.生成pdf文件的文件模板内容4.调用方法生成pdf 利用pdfmake生成pdf文件 1.下载安装pdfmake第三方包 npm i pdfma

零基础学习Redis(10) -- zset类型命令使用

zset是有序集合,内部除了存储元素外,还会存储一个score,存储在zset中的元素会按照score的大小升序排列,不同元素的score可以重复,score相同的元素会按照元素的字典序排列。 1. zset常用命令 1.1 zadd  zadd key [NX | XX] [GT | LT]   [CH] [INCR] score member [score member ...]

科研绘图系列:R语言扩展物种堆积图(Extended Stacked Barplot)

介绍 R语言的扩展物种堆积图是一种数据可视化工具,它不仅展示了物种的堆积结果,还整合了不同样本分组之间的差异性分析结果。这种图形表示方法能够直观地比较不同物种在各个分组中的显著性差异,为研究者提供了一种有效的数据解读方式。 加载R包 knitr::opts_chunk$set(warning = F, message = F)library(tidyverse)library(phyl