C语言应用层程序热补丁

一、热补丁简介

一个正在运行的程序,要是有某函数或某流程有问题,需要修改,有两个方式:

1.通过设置LD_PRELOAD把需要的库重新定向,但这种方式需要重启正在运行的程序。

2.通过修改可执行文件某个函数指向的地址,指向新的函数,这种方式可不重启正在运行的程序。这就热补丁(hotpatch)。

二、热补丁使用

1.API介绍

bash 复制代码
Linux提供了一套API来动态装载库。热补丁主要使用如下API:
- dlopen,打开一个库,并为使用该库作些准备。
- dlsym,在打开的库中查找符号的值。
- dlclose,关闭库。

在使用一下API时,要提前包含头文件**#include <dlfcn.h>**

c 复制代码
dlopen函数原型:
void *dlopen(const char *filename, int flags);
	--filename是你要打开的库路径
	--flags必须有RTLD_LAZY或者RTLD_NOW其中之一。
		RTLD_LAZY:在dlopen返回前,
				对于动态库中的未定义的符号不执行解析。(仅针对函数,变量立即解析)
		RTLD_NOW:需要在dlopen返回前,
		解析出所有未定义符号,如果解析不出来,在则会返回NULL和错误。
		***/*可以和一下参数进行搭配使用*/***
		--RTLD_GLOBAL:动态库中定义的符号可被其后打开的其它库解析。
		--RTLD_LOCAL:与RTLD_GLOBAL作用相反,动态库中定义的符号不能被其后打开的其它库重定位。
						如果没有指明是RTLD_GLOBAL还是RTLD_LOCAL,则缺省为RTLD_LOCAL。
		--RTLD_NODELETE:在dlclose()期间不卸载库,并且在以后使用dlopen()重新加载库时不初始化库中的静态变量。
		--RTLD_NOLOAD:不加载库。可用于测试库是否已加载(dlopen()返回NULL说明未加载,否则说明已加载),也可用于改变已加载库的flag。
		--RTLD_DEEPBIND:在搜索全局符号前先搜索库内的符号,避免同名符号的冲突。
返回值是句柄,在下面dlsym中使用。
c 复制代码
dlsym函数原型
void *dlsym(void *handle, const char *symbol);
		--handle是dlopen返回的句柄。
		--symbol是你想替换的符号。
返回值是指向替换后函数的地址,供调用使用。
c 复制代码
dlclose函数原型
int dlclose(void *handle);
注意用于关闭指定句柄的动态链接库,只有当此动态链接库的使用计数为0时,才会真正被系统卸载。

三、热补丁代码示例

要被替换的函数

该文件起名为1.c,要先编为1.so

bash 复制代码
gcc -fPIC -shared 1.c -o 1.so
c 复制代码
#include <stdio.h>

int func2()
{
	printf("1.c_func2\n");
	return 0;
}

int func1()
{
	func2();
	printf("1.c_func1\n");
	return 0;
}

需要替换的

该文件起名为2.c,编译的时候要加上 -ldl。

bash 复制代码
gcc 2.c -ldl
c 复制代码
#include <stdio.h>
#include <dlfcn.h>
#include <stdlib.h>

int func2()
{
	printf("2.c_func2\n");
	return 0;
}

int func1()
{
	func2();
	printf("2.c_func1\n");
	return 0;
}

int main()
{
	void * handle;
	/*定义一个函数指针指向原函数,这样就可以在后面一直使用原函数名了*/
	int  (*func)(void);
	func = func1;
	
	/*获得句柄*/
	handle = dlopen("./1.so", RTLD_LAZY);
	
	/*原函数*/
	func();
	sleep(1);
	func();
	sleep(1);

	/*进行函数替换*/
	*(void **)(&func)=dlsym(handle,"func1");

	/*这个时候在打印就已经变了*/
	func();
	dlclose(handle);
	return 0;
}

该程序输出

bash 复制代码
2.c_func2
2.c_func1
2.c_func2
2.c_func1
1.c_func2
1.c_func1

四、热补丁补充

1.根据上面代码,可以看出如果函数中包含别的函数,也是可以替换的。但是如果只有声明没有实现就会段错误。

2.不同进程的是不能替换的,就算是函数指针定义在最前面也不可以。

3.再编译的时候不能去符号,不要不能替换。

五、热补丁其他方法

1.找个空地址把新函数烧进去,然后修改原函数的入口地址。这个方法普遍使用于单片机上。

2.C语言调用lua脚本,通过lua脚本进行源代码的替换。这个办法需要业务程序也是lua语言写的。

如果大家还有别的办法,也请评论区给我说一下。

相关推荐
溟洵5 分钟前
【 C/C++ 算法】入门动态规划-----一维动态规划基础(以练代学式)
c语言·c++·算法
晨非辰1 小时前
#C语言——刷题攻略:牛客编程入门训练(十二):攻克 循环控制(四)、循环输出图形(一),轻松拿捏!
c语言·开发语言·经验分享·笔记·其他·学习方法·visual studio
Pretend° Ω16 小时前
LRU缓存详解:用C语言实现高效数据管理
运维·c语言·spring·缓存·lru·双向链表
我叫汪枫17 小时前
C语言深度入门系列:第十一篇 - 动态内存管理与数据结构:程序世界的高效算法大师
c语言·数据结构·算法
叫我木子21 小时前
c语言,识别到黑色就自动开枪,4399单击游戏狙击战场,源码分享,豆包ai出品
c语言·人工智能·游戏
l1t21 小时前
对expat库XML_Parse函数调用优化的测试
xml·c语言·解析器·expat
l1t1 天前
利用美团龙猫添加xlsx的sheet.xml读取sharedStrings.xml中共享字符串输出到csv功能
xml·c语言·数据结构·人工智能·算法·解析器
l1t1 天前
how to build tbox xml into the demo
xml·linux·c语言·parser·tbox
九皇叔叔1 天前
【2】标识符
c语言
野生的编程萌新1 天前
【C++深学日志】从0开始的C++生活
c语言·开发语言·c++·算法