Linux动态库*.so函数名修改

在某些学习或者特殊需求的情况下要对linux下动态库*.so文件内部的函数名进行修改。

        比如一个函数ADD(int a,int b);修改为Add(int a,int b);

通过这篇文章你将了解到在linux下动态库函数名寻址的规则,截止2024年3月linux动态库的寻址规则已经出现多种,这里不会一一介绍。这篇文章仅提供规则,并不提供修改函数名的相关代码和执行文件。如果有需要请留言沟通。

开发环境:Ubuntu20、gcc、g++、IDAPro(Windows下安装即可,版本不限)。

必备知识:ELF文件标准(linux下执行文件头)、IDApro的使用方法。

建议了解:linux 动态库加载流程(自行寻找资源,后续可能给出链接)参考文档(P73):https://paper.seebug.org/papers/Archive/refs/elf/Understanding_ELF.pdf

正文开始:

        编译简单的动态库和调用代码。

        以下为测试代码:其中仅有两个函数分别是ADD和MINUS的动态库代码;使用main.cpp链接api.so对这两个函数进行调用,并执行。最终再修改main.cpp对ADD的调用修改为Add,同时不编译动态库,仅对动态库的二进制文件进行修改的情况下,完成调用。

注:下列代码仅为参考,并未规范处理。编译命令见main.cpp。

//myAPI.h
//int ADD(int a, int b);
//int MINUS(int a, int b);#ifdef __cplusplus
extern "C" {
#endif// int Add(int a, int b);
int ADD(int a, int b);
int MINUS(int a, int b);#ifdef __cplusplus
}
#endif
//myAPI.cpp
#include "api.h"// int aaa(int a, int b){
int ADD(int a, int b){return a + b;
}
// int aaa(int a, int b){
//     return a + b;
// }
int MINUS(int a, int b){// int c = ADD(a,b);int c = b;return a - c;
}
//main.cpp
#include "api.h"
#include <iostream>
// #include <hash.h>
// 使用hash表做编译,不写使用ELF GUN hash 或者两者兼顾// g++ -shared -fPIC -o libapi.so api.cpp -Wl,--hash-style=sysv  /// -Wl,--retain-symbols-file=retain-symbols.txt
// 链接动态库生成执行文件// g++ -o main main.cpp api.h -L. -lapi
// 添加动态库路径到环境变量中// export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/.
int main(){std::cout << "1 + 1 = " << ADD(1, 1) << std::endl;std::cout << "1 - 1 = " << MINUS(1, 1) << std::endl;return 0;
}

通过上述步骤你将获得libapi.so和main两个可执行文件。并且执行main时,运行正常。

打开IDAPro,将libapi.so使用IAD进行逆向,能够查看二进制文件即可。

        idx    name         hash_val3 __cxa_finalize 0BEA6495 000000024 _Z5MINUSii 01E5E459 000000015 _ITM_registerTMCloneTable 0B7268A5 000000016 _ITM_deregisterTMCloneTable 012F7225 000000017 _Z3ADDii 0D758CB9 000000028 __gmon_start__ 0F4D007F 00000000ida pro 提供数据如下:; ELF Hash Tableelf_hash_nbucket DCD 3elf_hash_nchain DCD 9elf_hash_bucket DCD 8, 6, 7elf_hash_chain  DCD 0, 0, 0, 0, 0, 4, 5, 3, 0; ELF Symbol Table0  Elf64_Sym <0>1  Elf64_Sym <byte_300 - byte_300, 3, 0, 7, .init_proc, 0>2  Elf64_Sym <byte_300 - byte_300, 3, 0, 0x12, __dso_handle, 0>3  Elf64_Sym <aCxaFinalize - byte_300, 0x20, 0, 0, dword_0, 0> ; "__cxa_finalize"4  Elf64_Sym <aZ5minusii - byte_300, 0x12, 0, 9, _Z5MINUSii, 0x28> ; "_Z5MINUSii" ...5  Elf64_Sym <aItmRegistertmc - byte_300, 0x20, 0, 0, dword_0, 0> ; "_ITM_registerTMCloneTable"6  Elf64_Sym <aItmDeregistert - byte_300, 0x20, 0, 0, dword_0, 0> ; "_ITM_deregisterTMCloneTable"7  Elf64_Sym <aZ3addii - byte_300, 0x12, 0, 9, _Z3ADDii, 0x20> ; "_Z3ADDii" ...8  Elf64_Sym <aGmonStart - byte_300, 0x20, 0, 0, dword_0, 0> ; "__gmon_start__"; ELF String Tablebyte_300        DCB 0                   ; DATA XREF: LOAD:0000000000000240↑o; LOAD:0000000000000258↑o ...aGmonStart      DCB "__gmon_start__",0  ; DATA XREF: LOAD:00000000000002E8↑oaItmDeregistert DCB "_ITM_deregisterTMCloneTable",0; DATA XREF: LOAD:00000000000002B8↑oaItmRegistertmc DCB "_ITM_registerTMCloneTable",0; DATA XREF: LOAD:00000000000002A0↑oaCxaFinalize    DCB "__cxa_finalize",0  ; DATA XREF: LOAD:0000000000000270↑oaZ3addii        DCB "_Z3ADDii",0        ; DATA XREF: LOAD:00000000000002D0↑oaZ5minusii      DCB "_Z5MINUSii",0      ; DATA XREF: LOAD:0000000000000288↑oDCB 0, 0, 0, 0, 0, 0, 0

规则:

            规则:idx是直接读取 ELF Symbol Table 里面的 内容,顺序和 ELF String Table 里面的不同
                    hash_val 通过特定的方法计算得出。
                    elf_hash_nbucket 这个值编译器根据方 案计算出的,一般(总符号数/4 + 1) 在附近选择一个素数,能 够使数据更加离散。
                    elf_hash_nchain  这个值是符号的个数 。
                    elf_hash_bucket DCD 8, 6, 7
                        根据下表计算出的hash_val从最下面开始,第8个 0 ,接着第6个 1, 第7个是2。
                    elf_hash_chain  DCD 0, 0, 0, 0, 0, 4, 5, 3, 0
                        根据下表计算出:

                                     在计算的hash=0有 8,
                                         计算的hash=1有 6,5,4
                                         计算的hash=2有 7,3
                            因此:elf_hash_bucket[0] = 8;elf_hash_bucket[1] = 6;elf_hash_bucket[2] = 7;
                                    elf_hash_chain[8] = 0
                                    elf_hash_chain[6] = 5; elf_hash_chain[5] = 4; elf_hash_chain[4] = 0
                                    elf_hash_chain[7] = 3;

                对橙色部分的内容进行解释:

                 hash为0的只有8,结尾默认0,故hash_chain[8]=0。

                 hash为1的有6,5,4 结尾默认0,故hash_chain[6]=5,hash_chain[5]=4,hash_chain[4]=0

                 同理,hash为2的如上。

至此,你已经了解了--hash-style=sysv使用传统ELF编译动态库,hash表的生成规则了,其本质是一种快捷的链式结构。

使用十六进制编辑器,将hash表对应的函数ADD->Add,然后调整ELF hash链表。

            3 __cxa_finalize 0BEA6495 000000024 _Z5MINUSii 01E5E459 000000015 _ITM_registerTMCloneTable 0B7268A5 000000016 _ITM_deregisterTMCloneTable 012F7225 000000017 _Z3Addii 0D77ACB9 000000008 __gmon_start__ 0F4D007F 00000000hash_val = 0: 8,7hash_val = 1: 6,5,4hash_val = 2: 3elf_hash_bucket DCD 8, 6, 3elf_hash_chain  DCD 0, 0, 0, 0, 0, 4, 5, 0, 7

注:使用IDA获取这个表的时候,有对应值在二进制文件的偏移,可以根据偏移直接修改。这一过程修改了hash table 和 string table两部分,上面首为7的行,已经和最开始的不一致了,是通过elf_hash(string)获得到的。这个函数的实现,资源较多。稍后进行链接给出。

调整完毕,再将main.cpp和api.h两个文件的ADD->Add。如下:

//myAPI.h
//int ADD(int a, int b);
//int MINUS(int a, int b);#ifdef __cplusplus
extern "C" {
#endif// int Add(int a, int b);
int ADD(int a, int b);
int MINUS(int a, int b);#ifdef __cplusplus
}
#endif
//----------------------------------
//main.cpp
#include "api.h"
#include <iostream>
// #include <hash.h>
// 使用hash表做编译,不写使用ELF GUN hash 或者两者兼顾// g++ -shared -fPIC -o libapi.so api.cpp -Wl,--hash-style=sysv  /// -Wl,--retain-symbols-file=retain-symbols.txt
// 链接动态库生成执行文件// g++ -o main main.cpp api.h -L. -lapi
// 添加动态库路径到环境变量中// export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/.
int main(){std::cout << "1 + 1 = " << Add(1, 1) << std::endl;std::cout << "1 - 1 = " << MINUS(1, 1) << std::endl;return 0;
}

此时,使用修改后的libapi.so和上述两个文件,编译,发现Add可以被正常调用。

结语:此案例中ADD->Add是函数名长度,所以可以避免下面执行的代码不做偏移修改,如果函数名长度不一致,可能导致整个动态库文件出现较大的问题。如果有兴趣,可以查找相关的资料并提交到GNU,说不定你也是对开源社区做贡献的小可爱了。这个案例对有传统ELF hash表有效,其他标准的hash表也是类似的调整方法,了解其运作原理就可以很快出结果。

参考文档(P73):https://paper.seebug.org/papers/Archive/refs/elf/Understanding_ELF.pdf

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

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

相关文章

C++语法、Linux命令查询网站

文章目录 1.cplusplus2.cppreference3.Linux命令查询网站 1.cplusplus 网址&#xff1a;https://legacy.cplusplus.com/ 2.cppreference 1.cppreference中文网站&#xff1a;https://zh.cppreference.com/w/首页 2.cppreference英文原站&#xff1a;https://en.cppreference…

服务器生产环境问题解决思路

游戏服务器开发节奏比较快,版本迭代很频繁,有一些项目甚至出现了周更新(每周准时停服更新维护)。由于功能开发时间短,研发人员本身技术能力等原因,线上出现bug很常见。笔者经历过的游戏项目,一年到头没几次更新不出现bug的(当然,配置问题也算bug)。那当出现bug,我们…

C++ ezOptionParse的用法

在网上找了一下&#xff0c;发现并没有很多关于这个小型头文件的资料 只好自己上了 int main(int argc, const char * argv[]) {ezOptionParser opt;opt.overview "Demo of parsers features."; //概要说明,一般需要填写&#xff0c;简要介绍程序的作用opt.synta…

Java获取视频封面图,利用FFmpegFrameGrabber获取视频封面图

依赖 <dependency><groupId>org.bytedeco</groupId><artifactId>javacv-platform</artifactId><version>1.5.9</version></dependency>传入视频流获取图片byte /*** 获取视频截图** param frameNumber 视频的指定帧数* param …

关于Qt中的信号与槽

在Qt中&#xff0c;信号与槽是一种用于对象间通信的机制。信号是在特定事件发生时被发射的&#xff0c;而槽是在接收到信号时执行的函数。通过信号与槽&#xff0c;一个对象可以告诉其他对象某个事件已经发生&#xff0c;并提供有关该事件的信息。 以下是关于Qt中信号与槽的一…

Unity AI Navigation插件快速使用方法

AI Navigation插件使您能够创建能够在游戏世界中智能移动的角色。这些角色利用的是根据场景几何结构自动生成的导航网格。障碍物可以让您在运行时改变角色的导航路径。 演示使用的Unity版本为Tuanjie 1.0.0,团结引擎是Unity中国的引擎研发团队基于Unity 2022 LTS版本为中国开发…

C#,图论与图算法,双连通图(Biconnected Components of Graph)的算法与源代码

1 双连通图(Biconnected Components of Graph) 如果任意两个顶点之间有两条顶点不相交的路径,则无向图称为双连通图。在双连通图中,有一个通过任意两个顶点的简单循环。 按照约定,由边连接的两个节点构成双连通图,但这并不验证上述属性。对于具有两个以上顶点的图,必须…

Python Learn day05

Python Learn day05 本文主要讲解 继承、多态、定制类 继承和多态 什么是继承 当新类想要拥有现有类的功能结构&#xff0c;可以使用继承。继承的前提是新类 is a 现有类&#xff0c;即&#xff1a; 子类 is 父类 总是从某个类继承&#xff1a; class Myclass(object):pass…

Hello,Spider!入门第一个爬虫程序

在各大编程语言中&#xff0c;初学者要学会编写的第一个简单程序一般就是“Hello, World!”&#xff0c;即通过程序来在屏幕上输出一行“Hello, World!”这样的文字&#xff0c;在Python中&#xff0c;只需一行代码就可以做到。我们把这第一个爬虫就称之为“HelloSpider”&…

springcloud五大组件:Eureka:注册中心、Zuul:服务网关、Ribbon:负载均衡、Feign:服务调用、Hystix:熔断器

你提到的这些组件都是Spring Cloud生态系统中非常关键和常用的组件。但是&#xff0c;关于这些组件的命名&#xff0c;有一点需要更正&#xff1a; Eureka 应该是 Eureka 或者 Eureka Server&#xff1a;Eureka是Netflix开源的一个服务发现组件&#xff0c;它本身是一个基于RE…

【Git】Github 上commit后,绿格子contribution却不显示?不知道怎么弥补?解决方法在这里

github 上commit后&#xff0c;绿格子&#xff08;contribution&#xff09;却不显示 问题描述 今天一直在github上面commit代码&#xff0c;但是github中并没有显示自己的contribution&#xff08;没有绿色的格子&#xff09;&#xff0c;全是空白&#xff0c;网上一查是因为…

免费分享一套SpringBoot+Vue自习室(预约)管理系统,帅呆了~~

大家好&#xff0c;我是java1234_小锋老师&#xff0c;看到一个不错的SpringBootVue自习室预约)管理系统&#xff0c;分享下哈。 项目视频演示 【免费】SpringBootVue自习室预约(预约)管理系统 Java毕业设计_哔哩哔哩_bilibili【免费】SpringBootVue自习室预约(预约)管理系统…

大语言模型(LLM)Token 概念

1. 概念&#xff1a; Token概念&#xff1a;在大型语言模型中&#xff0c;Token是对输入文本进行分割和编码时的最小单位。它可以是单词、子词、字符或其他形式的文本片段。 2. 分类&#xff1a; 词级Token&#xff08;Word-level Tokens&#xff09;&#xff1a;将文本分割…

Java项目企业设备管理系统

java项目企业设备管理系统javaweb项目ssm框架项目 运行环境:idea/eclipse tomcat jdk mysql navicat 系统用户分为员工和管理员两类用户。两类用户都可以进行系统的登录&#xff0c;虽然进入的系统主页结构相似&#xff0c;但是在功能上有不同。员工的密码可以自己进入系统后…

【目标检测-数据集准备】DIOR转为yolo训练所需格式

【目标检测】DIOR遥感影像数据集&#xff0c;转为yolo系列模型训练所需格式。 标签文件位于Annotations下&#xff0c;格式为xml&#xff0c;yolo系列模型训练所需格式为txt&#xff0c;格式为 class_id x_center,y_center,w,h其中&#xff0c;train&#xff0c;text&#xff…

企业微信托管集成语聚AI,做AI智能客服助手,实现精准回答用户问题、创建群组自动化场景

场景描述 当前商业环境下&#xff0c;企业正面临着为客户提供高效而精确服务的迫切需求&#xff0c;这是提升客户满意度和增强品牌忠诚的关键。随着互联网时代的到来&#xff0c;无论是企业还是个人&#xff0c;都在努力加强与顾客的连接和互动&#xff0c;以期达到卓越的用户…

Python笔记|字符串的转义

重新梳理一遍python的基础知识 除了数字&#xff0c;Python 还可以操作字符串。字符串有多种表现形式&#xff0c;用单引号&#xff08;……&#xff09;或双引号&#xff08;"……"&#xff09;标注的结果相同 。反斜杠 \ 用于转义&#xff1a; >>>spam e…

摩尔定律对于计算机行业有什么重要意义?

摩尔定律对计算机行业有着极其深远的影响&#xff0c;主要体现在以下几个方面&#xff1a; 技术进步预期&#xff1a; 摩尔定律提供了一个指导性的预测模型&#xff0c;表明处理能力大约每两年翻一番。这个预期帮助行业对未来的技术发展和市场需求进行规划。 硬件性能提升&…

java抽象类的作用及解析

在 Java 中&#xff0c;抽象类是一种特殊的类&#xff0c;它可以用于定义一些抽象的方法和属性&#xff0c;这些方法和属性可能在子类中有不同的实现。 抽象类的主要作用包括&#xff1a; 提供抽象方法&#xff1a;抽象类可以包含一些没有具体实现的抽象方法&#xff0c;这些…

3 数据分析--Numpy

Numpy&#xff1a;用于存储和处理大型矩阵&#xff0c;比python自身的嵌套列表结构要高校很多 anaconda&#xff1a;是一个开源的python发行版本&#xff0c;包括conda&#xff0c;python以及一大堆安装好的工具包 ipython 开发环境&#xff0c;解释器 jupyter notebook是编辑…