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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/20573.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【技术实操】银河高级服务器操作系统实例分享,数据库日志文件属主不对问题分析

1. 问题现象描述 2023 年 06 月 30 日在迁移数据库过程中&#xff0c;遇到数据库 crash 的缺陷&#xff0c;原因如下&#xff1a;在数据库启动时候生成的一组临时文件中&#xff0c;有 owner 为 root 的文件&#xff0c; 文件权限默认为 640&#xff0c; 当数据库需要使用的时…

「C系列」C 基本语法

文章目录 一、C 基本语法1. **程序结构**2. **数据类型**3. **变量声明**4. **运算符**6. **函数**7. **指针**8. **数组**9. **结构体和联合体**10. **预处理指令**11. **内存管理** 二、C 关键字1. 整体概览2. 具体关键字数据类型关键字控制流关键字其他关键字C11新增关键字总…

高速服务区智慧公厕管理系统引导屏UI界面展示

在现代社会&#xff0c;高速服务区作为人们出行途中的重要休憩场所&#xff0c;其各项设施的智能化水平也在不断提升。其中&#xff0c;智慧公厕管理系统的出现&#xff0c;为人们带来了更加便捷、舒适的如厕体验&#xff0c;而引导屏 UI 界面更是这一系统的重要展示窗口。 智慧…

mdk 编程入门:探索编程世界的神秘之旅

mdk 编程入门&#xff1a;探索编程世界的神秘之旅 在科技日新月异的今天&#xff0c;编程已成为一项不可或缺的技能。MDK编程作为其中的一员&#xff0c;以其独特的魅力和广泛的应用领域吸引着越来越多的初学者。那么&#xff0c;如何入门MDK编程呢&#xff1f;本文将带你走进…

Python | Leetcode Python题解之第126题单词接龙II

题目&#xff1a; 题解&#xff1a; class Solution:def findLadders(self, beginWord: str, endWord: str, wordList: List[str]) -> List[List[str]]:ans []if endWord not in wordList:return anssize len(beginWord)cur_word_set {beginWord}ws set(wordList)# 用于…

MySQL开发教程和具体应用案例

一、MySQL开发教程 初识数据库 定义:数据仓库,安装在操作系统之上,用于存储和管理数据。 分类:关系型数据库(如MySQL、Oracle、SQL Server)和非关系型数据库(如Redis、MongoDB)。 SQL:结构化查询语言,用于管理和操作关系型数据库。 操作数据库 创建、修改、删除…

【LeetCode 637】二叉树的层平均值

1. 题目 2. 分析 没啥好说的&#xff0c;这题很简单&#xff0c;希望能在5min内搞定。遇到问题要快速排查&#xff0c;不要怀疑编译器。 3. 代码 # Definition for a binary tree node. # class TreeNode: # def __init__(self, val0, leftNone, rightNone): # …

nacos配置发布流程代码示例

在 Nacos 中发布配置通常不需要直接编写代码&#xff0c;而是通过 Nacos 的管理界面或者使用 Nacos 提供的客户端 SDK 来完成。不过&#xff0c;如果想要通过编程的方式来发布配置&#xff0c;可以使用 Nacos 的客户端 SDK。 以下是一个使用 Java 和 Nacos 客户端 SDK 发布配置…

如何在 Linux VPS 上自定义你的 Bash 提示符

前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家。点击跳转到网站。 简介 在管理 Linux 服务器时,您将花费大量时间使用命令行。对于大多数人来说,这意味着与 Bash shell 一起花费大量时间。 虽然大多数发行版为用户和 root 提供了合理…

WPF Binding对象、数据校验、数据转换

在WinForm中&#xff0c;我们要想对控件赋值&#xff0c;需要在后台代码中拿到控件对象进行操作&#xff0c;这种赋值形式&#xff0c;从根本上是无法实现界面与逻辑分离的。 在WPF中&#xff0c;微软引入了Binding对象&#xff0c;通过Binding&#xff0c;我们可以直接将控件与…

CSS双飞翼布局

双飞翼布局是一种经典的CSS布局模式&#xff0c;主要用于实现左右两列固定宽度&#xff0c;中间列自适应的布局。 比如&#xff1a;写一个左中右布局占满全屏&#xff0c;其中左、右两块固定宽 200px&#xff0c;中间自适应&#xff0c;要求先加载中间块。 <!DOCTYPE html…

启动u盘恢复成普通u盘

DiskUtility&#xff08;磁盘工具&#xff09;恢复可启动U盘为普通存储设备。点击顶部菜单栏的"抹掉"按钮。 u盘启动盘怎么恢复成u盘

Re0:从零开始的C++游戏开发【中】

Re0:从零开始的C游戏开发 &#xff08;中&#xff09; 这是蒟蒻观看B站upVoidmatrix的课程从零开始的提瓦特幸存者的个人笔记【自用】 前言&#xff1a;采用适用于小白的easyx图形库。 第三集 提瓦特の幸存者 3.1 程序动画实现及角色移动 在开始之前&#xff0c;我们应该认…

如何使用Dora SDK完成Fragment流式切换和非流式切换

我想大家对Fragment都不陌生&#xff0c;它作为界面碎片被使用在Activity中&#xff0c;如果只是更换Activity中的一小部分界面&#xff0c;是没有必要再重新打开一个新的Activity的。有时&#xff0c;即使要更换完整的UI布局&#xff0c;也可以使用Fragment来切换界面。 何…

ArrayList和LinkedList对比,ArrayList使用注意事项

ArrayList和LinkedList对比&#xff0c;ArrayList使用注意事项 ArrayList 和 LinkedList 是 Java 中常用的两种集合类&#xff0c;它们在内部实现和性能上有一些重要的区别。 ArrayList: ArrayList 是基于动态数组实现的。它内部使用一个数组来存储元素&#xff0c;当数组空间…

Vue3实战笔记(59)—从零开始掌握Vue3插槽机制,进阶与提高

文章目录 前言一、具名插槽二、高级列表组件示例总结 前言 接上文&#xff0c;接下来看一点稍微复杂的&#xff1a;具名插槽 一、具名插槽 子组件 MyComponent.vue&#xff1a; <template><div><slot name"header"></slot><slot><…

点到线段的最短矩离 及垂足的计算

过P做MN的垂线&#xff0c;垂足为Q&#xff0c;若Q在线段MN以内(包括与点M点N重合)&#xff0c;则最短距离为垂线段长度&#xff0c;若垂足在MN以外&#xff0c;则最短距离为PM&#xff0c;PN中的较小者。&#xff08;若P与MN共线&#xff0c;垂线长度为零&#xff0c;同样适用…

[机器学习] 低代码机器学习工具PyCaret库使用指北

PyCaret是一个开源、低代码Python机器学习库&#xff0c;能够自动化机器学习工作流程。它是一个端到端的机器学习和模型管理工具&#xff0c;极大地加快了实验周期&#xff0c;提高了工作效率。PyCaret本质上是围绕几个机器学习库和框架&#xff08;如scikit-learn、XGBoost、L…

每天半小时,让大脑变得更聪明

晚上好。 今天的文章&#xff0c;我想跟你分享一套简单又有效的「大脑保健操」。每天只需要很短的时间&#xff0c;就能有效地锻炼和强化你的脑力。 这些都是我践行了许多年的习惯&#xff0c;也有切实的实验和理论基础作为依托。 你不需要每天都完全按照这套模式来执行&#x…

Kafka篇:Kafka搭建、使用、及Flink整合Kafka文档

一、Kafka搭建 1、上传并解压改名 tar -xvf kafka_2.11-1.0.0.tgz mv kafka_2.11-1.0.0 kafka-1.0.0 2、配置环境变量 vim /etc/profile export KAFKA_HOME/usr/local/soft/kafka-1.0.0 export PATH$PATH:$KAFKA_HOME/bin source /etc/profile &#xff08;使环境变量生效…