基础技术-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

相关文章

JavaScript中的reduce方法执行过程、使用场景及进阶用法

《JavaScript中的reduce方法执行过程、使用场景及进阶用法》:本文主要介绍JavaScript中的reduce方法执行过程、使用场景及进阶用法的相关资料,reduce是JavaScri... 目录1. 什么是reduce2. reduce语法2.1 语法2.2 参数说明3. reduce执行过程

如何使用Java实现请求deepseek

《如何使用Java实现请求deepseek》这篇文章主要为大家详细介绍了如何使用Java实现请求deepseek功能,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录1.deepseek的api创建2.Java实现请求deepseek2.1 pom文件2.2 json转化文件2.2

python使用fastapi实现多语言国际化的操作指南

《python使用fastapi实现多语言国际化的操作指南》本文介绍了使用Python和FastAPI实现多语言国际化的操作指南,包括多语言架构技术栈、翻译管理、前端本地化、语言切换机制以及常见陷阱和... 目录多语言国际化实现指南项目多语言架构技术栈目录结构翻译工作流1. 翻译数据存储2. 翻译生成脚本

C++ Primer 多维数组的使用

《C++Primer多维数组的使用》本文主要介绍了多维数组在C++语言中的定义、初始化、下标引用以及使用范围for语句处理多维数组的方法,具有一定的参考价值,感兴趣的可以了解一下... 目录多维数组多维数组的初始化多维数组的下标引用使用范围for语句处理多维数组指针和多维数组多维数组严格来说,C++语言没

在 Spring Boot 中使用 @Autowired和 @Bean注解的示例详解

《在SpringBoot中使用@Autowired和@Bean注解的示例详解》本文通过一个示例演示了如何在SpringBoot中使用@Autowired和@Bean注解进行依赖注入和Bean... 目录在 Spring Boot 中使用 @Autowired 和 @Bean 注解示例背景1. 定义 Stud

使用 sql-research-assistant进行 SQL 数据库研究的实战指南(代码实现演示)

《使用sql-research-assistant进行SQL数据库研究的实战指南(代码实现演示)》本文介绍了sql-research-assistant工具,该工具基于LangChain框架,集... 目录技术背景介绍核心原理解析代码实现演示安装和配置项目集成LangSmith 配置(可选)启动服务应用场景

使用Python快速实现链接转word文档

《使用Python快速实现链接转word文档》这篇文章主要为大家详细介绍了如何使用Python快速实现链接转word文档功能,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 演示代码展示from newspaper import Articlefrom docx import

oracle DBMS_SQL.PARSE的使用方法和示例

《oracleDBMS_SQL.PARSE的使用方法和示例》DBMS_SQL是Oracle数据库中的一个强大包,用于动态构建和执行SQL语句,DBMS_SQL.PARSE过程解析SQL语句或PL/S... 目录语法示例注意事项DBMS_SQL 是 oracle 数据库中的一个强大包,它允许动态地构建和执行

0基础租个硬件玩deepseek,蓝耘元生代智算云|本地部署DeepSeek R1模型的操作流程

《0基础租个硬件玩deepseek,蓝耘元生代智算云|本地部署DeepSeekR1模型的操作流程》DeepSeekR1模型凭借其强大的自然语言处理能力,在未来具有广阔的应用前景,有望在多个领域发... 目录0基础租个硬件玩deepseek,蓝耘元生代智算云|本地部署DeepSeek R1模型,3步搞定一个应

SpringBoot中使用 ThreadLocal 进行多线程上下文管理及注意事项小结

《SpringBoot中使用ThreadLocal进行多线程上下文管理及注意事项小结》本文详细介绍了ThreadLocal的原理、使用场景和示例代码,并在SpringBoot中使用ThreadLo... 目录前言技术积累1.什么是 ThreadLocal2. ThreadLocal 的原理2.1 线程隔离2