libco hook原理简析
我们先看一下libco协程库的特性描述
libco的特性
无需侵入业务逻辑,把多进程、多线程服务改造成协程服务,并发能力得到百倍提升;
支持CGI框架,轻松构建web服务(New);
支持gethostbyname、mysqlclient、ssl等常用第三库(New);
可选的共享栈模式,单机轻松接入千万连接(New);
对于其第三点特性,支持gethostbyname、mysqlclient、ssl等常用第三库。这说明什么?说明它们的网络IO函数,使用的是libco中的网络IO函数,不然进入不了协程调度。那么lobco是如何实现的呢?如果你善于运用搜索引擎,一定会找到一些文章这样解释:因为libco协程库hook了系统的socket相关函数。
上面那句话,其实说了等于没说... 这是果,而不是因,而这也是我打算写这篇文章的原因,此文的相关版本最早发布在公司内部论坛,时间在17年初,由于x*&#%#(一堆敏感词),此文为裁剪版。
答案之一在co_sys_hook_call.cpp文件中,在该文件中实现了hook 系统socket相关函数的第一步(注意是第一步,还有后续条件),在该文件中,可看到其定义的大量与系统socket相关函数同名,同参数的函数,如下文的socket函数所示
//co_sys_hook_call.cpp
...
int socket(int domain, int type, int protocol)
{
HOOK_SYS_FUNC( socket );
if( !co_is_enable_sys_hook() )
{
return g_sys_socket_func( domain,type,protocol );
}
int fd = g_sys_socket_func(domain,type,protocol);
if( fd < 0 )
{
return fd;
}
rpchook_t *lp = alloc_by_fd( fd );
lp->domain = domain;
fcntl( fd, F_SETFL, g_sys_fcntl_func(fd, F_GETFL,0 ) );
return fd;
}
...
对于每一个其定义的同步socket相关函数,该文件中也定义了一个对应的函数指针
static socket_pfn_t g_sys_socket_func = (socket_pfn_t)dlsym(RTLD_NEXT,"socket");
细心的读者想必注意到了上面的socket函数,其第一行的
HOOK_SYS_FUNC( socket );
我们看一下HOOK_SYS_FUNC是干嘛的。
#define HOOK_SYS_FUNC(name) if( !g_sys_##name##_func ) { g_sys_##name##_func = (name##_pfn_t)dlsym(RTLD_NEXT,#name); }
结合上面的函数指针定义,我们可以知道该宏用于初始化对应的函数指针。对于dlsym,它是用于从加载进内存的动态库中(通过传入其句柄)寻找指定符号的函数或者变量,这里的关键点是传入的句柄参数是RTLD_NEXT,该参数表示从当前库之后的load进来的动态库中寻找该符号。比如对于socket函数,如果没有其它库也定义了该函数的话,在这里找到的会是glibc中相关socket函数的地址。
我们可以看到libco在自己的socket函数中加入了一些额外的逻辑,然后最终调用的还是系统库中的socket函数。这成功的实现了hook的第一步。
接下来分析hook的第二步,也就是如何保证第三方库调用的是libco自身实现的相关socket函数呢?
简单而言,就是通过调整最终生成的可执行文件的链接顺序,使其全局符号表中的跟socket相关函数的符号为libco协程库中的符号。
这里需要简述一下目标文件生成最终的可执行文件的链接过程。
我们先简单看一下链接过程中会发生什么。在链接过程中,链接器会按顺序扫描输入的目标文件,将其中的符号加入全局符号表,再计算出合并后的各个段的长度和位置。之后将进行符号解析和重定位。
另外对于动态链接来说,其还将遵循如下一条规则
全局符号介入
linux下的动态链接器存在以下原则:当共享对象被load进来的时候,它的符号表会被合并到进程的全局符号表中(这里说的全局符号表并不是指里面的符号全部是全局符号,而是指这是一个汇总的符号表),当一个符号需要加入全局符号表时,如果相同的符号名已经存在,则后面加入的符号被忽略。
由于glibc是c/cpp程序的运行库,因此它是最后以动态链接的形式链接进来的,我们可以保证其肯定是最后加入全局符号表的,由于全局符号介入机制,glibc中的相关socket函数符号被忽略了(但是libco中巧妙的运用RTLD_NEXT参数获取到了其地址),也因此只要最终的可执行文件链接了libco协程库,就可以基本保证相关的socket函数被hook掉了。
为什么是基本保证?根据我的使用经验,有socket相关函数定义的动态库,不止glibc,pthread库中也有(不确定其它库是否也有)!不过也没有关系,只需要保证libco库位于pthread库之前链接即可。如下所示
gcc main.c -o test -LSOME_PATH -llibco -lpthread
另外我们可以看到在co_hook_sys_call.cpp文件的末尾有一个很有意思的函数
void co_enable_hook_sys() //这函数必须在这里,否则本文件会被忽略!!!
{
stCoRoutine_t *co = GetCurrThreadCo();
if( co )
{
co->cEnableSysHook = 1;
}
}
这里放这个函数其实是针对静态链接的情况,因为如果该文件生成的目标文件中的符号如果最终全部都没有被强引用到的话(比如在一个.h文件中对某个函数进行声明,那么是对这个函数符号的弱引用,而对这个函数进行调用,则是对这个函数符号的强引用),那么该目标文件在静态链接的过程中会被忽略掉。早期的计算机内存都非常小,因此能省一点是一点。
到这里基本就分析完毕了。另外假如你使用的是libco的动态库的话,可以通过readelf -d | grep 'NEEDED' 目标文件命令或者ldd 目标文件,查看当前动态库的链接顺序,确保hook掉了socket相关函数。
libco hook原理简析的更多相关文章
- Java Android 注解(Annotation) 及几个常用开源项目注解原理简析
不少开源库(ButterKnife.Retrofit.ActiveAndroid等等)都用到了注解的方式来简化代码提高开发效率. 本文简单介绍下 Annotation 示例.概念及作用.分类.自定义. ...
- PHP的错误报错级别设置原理简析
原理简析 摘录php.ini文件的默认配置(php5.4): ; Common Values: ; E_ALL (Show all errors, warnings and notices inclu ...
- Java Annotation 及几个常用开源项目注解原理简析
PDF 版: Java Annotation.pdf, PPT 版:Java Annotation.pptx, Keynote 版:Java Annotation.key 一.Annotation 示 ...
- [转载] Thrift原理简析(JAVA)
转载自http://shift-alt-ctrl.iteye.com/blog/1987416 Apache Thrift是一个跨语言的服务框架,本质上为RPC,同时具有序列化.发序列化机制:当我们开 ...
- Spring系列.@EnableRedisHttpSession原理简析
在集群系统中,经常会需要将Session进行共享.不然会出现这样一个问题:用户在系统A上登陆以后,假如后续的一些操作被负载均衡到系统B上面,系统B发现本机上没有这个用户的Session,会强制让用户重 ...
- SIFT特征原理简析(HELU版)
SIFT(Scale-Invariant Feature Transform)是一种具有尺度不变性和光照不变性的特征描述子,也同时是一套特征提取的理论,首次由D. G. Lowe于2004年以< ...
- 基于IdentityServer4的OIDC实现单点登录(SSO)原理简析
写着前面 IdentityServer4的学习断断续续,兜兜转转,走了不少弯路,也花了不少时间.可能是因为没有阅读源码,也没有特别系统的学习资料,相关文章很多园子里的大佬都有涉及,有系列文章,比如: ...
- Android热补丁技术—dexposed原理简析(手机淘宝采用方案)
上篇文章<Android无线开发的几种常用技术>我们介绍了几种android移动应用开发中的常用技术,其中的热补丁正在被越来越多的开发团队所使用,它涉及到dalvik虚拟机和android ...
- Android热补丁技术—dexposed原理简析(阿里Hao)
本文由嵌入式企鹅圈原创团队成员.阿里资深工程师Hao分享. 上篇文章<Android无线开发的几种常用技术>我们介绍了几种android移动应用开发中的常用技术,其中的热补丁正在被越来越多 ...
随机推荐
- Telegraf+Influxdb+Grafana自动化运维监控
概述:Telegraf收集信息,influxdb时序数据库存储数据,grafana平台展示数据,并进行监控告警,组成一个自动化运维监控平台. 一.influxdb InfluxDB是一个由Infl ...
- idea提交svn不显示新建文件
在idea中,使用svn提交时可能会出现 预期文件没出现在提交目录里. 是因为没有把新建文件添加到版本控制里. 解决办法:右键选择文件→subversion→add to vcs. 自动把新文件添加 ...
- spring mvc与mybatis与maven+mysql框架整合
<%@ page language="java" import="java.util.*" pageEncoding="utf-8"% ...
- Linux嵌入式学习-网络配置-ping外网、主机和域名
之前用的nfs挂载的文件系统,今天用yaffs2制作的文件系统并写入到nandflash中.但是网络却无法使用了. 首先,我们配置网卡. ifconfig eth0 192.168.1.230 bro ...
- SpringBoot全局异常拦截
SpringBoot全局异常捕获 使用到的技能 @RestControllerAdvice或(@ControllerAdvice+@ResponseBody) @ExceptionHandler 代码 ...
- JavaScript--总结一(变量+数据类型+运算符)
JavaScript是什么? 是一门脚本语言(不需要编译,直接执行) 是一门解释性语言 是一门动态类型的语言 是一门基于对象的语言 JavaScript分为三个部分 1.ECMAScript 标准- ...
- HTTP ERROR400的问题解决
今天写添加功能,在点添加提交时报了一个"HTTP ERROR 400"的错误,如图, 请求提交的代码死活跳转不到后台,郁闷中,开启debug功能,开始一步步排查, 1.先单独把跳转 ...
- 技术面试没过,居然是没有用pytest测试框架
1.引言 我有一个朋友是做Python自动化测试的.前几天他告诉我去参加一个大厂面试被刷了. 我问他是有没有总结被刷下来的原因.他说面试官问了一些 pytest 单元测试框架相关的知识,包括什么插件系 ...
- FTP服务器的搭建和使用(centos7)
1.显示如下图则表示已安装vsftp软件 如过没有则可以通过yum源进行安装 yum install -y vsftpd 操作:service vsftpd start|stop|restart 2. ...
- 解决phpmyadmin上传文件大小限制的配置方法
解决phpmyadmin上传文件大小限制的配置方法 phpmyadmin导入SQL文件时涉及到phpmyadmin上传文件大小限制问题,默认phpmyadmin上传文件大小为2M,如果想要phpmya ...