hook io异常注入

文中code https://gitee.com/bbjg001/darcy_common/tree/master/io_hook

需求引入

最近工作需要,需要验证一下我们的服务在硬盘故障下的鲁棒性。

从同事大佬哪里了解到hook技术,可以通过LD_PRELOAD这个环境变量拦截依赖库的调用链,将对标准库函数的调用转移到自己自定义的函数,然后返回自定义的错误代码。

使用方式

export LD_PRELOAD=hook_lib.so
./main
# hook_lib.so是自行编译的用来拦截的so文件
# ./main是要运行的二进制文件

一个简单的例子

有这样一个简单的main函数

// main.cpp
#include <fcntl.h>
#include <unistd.h>
#include <iostream>
#include <string.h>int main()
{int rfd = open("test.txt", O_RDONLY);std::cout << "open(), with ret: " << rfd << ", err(" << errno << "): " << strerror(errno) << std::endl;close(rfd);
}

如果test.txt不存在,输出是这样的

在这里插入图片描述

如果test.txt是一个存在的正常文件,输出

在这里插入图片描述

下面自定义open函数,通过hook拦截调用链调用自己的open函数,找到open函数的定义

在这里插入图片描述

自定义open函数,注意函数传参要与原open函数一致

// hook_lib.cpp
#include <unistd.h>  
#include <iostream> 
#include <dlfcn.h>
#include <fcntl.h>typedef int(*OPEN)(const char *__path, int __oflag, ...);int open(const char *__path, int __oflag, ...){std::cout << "!!! open函数被拦截了" << std::endl;errno = 2;return -1;
}

正常编译并运行main.cpp

g++ main.cpp -o main && ./main

输出是正常的

在这里插入图片描述

下面拦截注入自己的open函数

# 把自己的函数文件编译成.so文件
g++ --shared -fPIC  hook_lib.cpp -o hook_lib.so -ldl
# 通过LD_PRELOAD拦截调用链启动main函数
LD_PRELOAD=./hook_lib.so ./main

将hook函数的文件编译成.so文件

g++ --shared -fPIC  hook_lib.cpp -o hook_lib.so -ldl

在启动时通过LD_PRELOAD指定hook的库文件

g++ main.cpp -o main
LD_PRELOAD=./hook_lib.so ./main

在这里插入图片描述

进一步的做更多自定义的逻辑

这次以write函数为例

返回正常的write函数

可以定义在某些情况下返回错误码,某些情况下返回正常的write函数。这里通过随机概率返回两者。

hook逻辑

// hook_lib
extern ssize_t std_write (int __fd, __const void *__buf, size_t __n) {static void *handle = NULL;static WRITE old_write = NULL;if (!handle) {handle = dlopen("libc.so.6", RTLD_LAZY);old_write = (WRITE)dlsym(handle, "write");}return old_write(__fd, __buf, __n);
}
// 模拟的write函数
extern ssize_t write (int __fd, __const void *__buf, size_t __n) {if (rand() % 100 / 100.0 > 0.5) {errno = 2;return -1;}return std_write(__fd, __buf, __n);
}

main函数

// main
int main(int argc, char *argv[]){srand(time(NULL));const char *f_path = "test.txt";int fd = open(f_path, O_WRONLY | O_CREAT | O_TRUNC, 0644);for (int i = 0; i < 10; i++){int ret = write(fd, "HelloWorld", 10);if (ret < 0){std::cout << "write(), with ret: " << ret << ", err_info: " << strerror(errno) << std::endl;}else{std::cout << "write(), with ret: " << ret << std::endl;}}close(fd);return 0;
}

执行结果

$ LD_PRELOAD=./hook_lib.so ./main
write(), with ret: -1, err_info: No such file or directory
write(), with ret: 10
write(), with ret: -1, err_info: No such file or directory
write(), with ret: -1, err_info: No such file or directory
write(), with ret: 10
write(), with ret: -1, err_info: No such file or directory
write(), with ret: -1, err_info: No such file or directory
write(), with ret: 10
write(), with ret: -1, err_info: No such file or directory
write(), with ret: 10

控制注入异常的path

hook逻辑

// hook_lib
// 检查当前操作的文件是否是要注入异常的文件
bool is_current_path(int fd, std::string path){    if(path==""){return false;}// get current pathchar buf[256] = {'\0'};char _file_path[256] = {'\0'};std::string file_path;snprintf(buf, sizeof (buf), "/proc/self/fd/%d", fd);if (readlink(buf, _file_path, sizeof(_file_path) - 1) != -1) {file_path = _file_path;}if(file_path.find(path) != std::string::npos){  // 路径中包含${path}即被命中return true;}return false;
}extern ssize_t write (int __fd, __const void *__buf, size_t __n) {if (is_current_path(__fd, "test")) {errno = 2;return -1;}return std_write(__fd, __buf, __n);
}

main函数

// main
int main(int argc, char *argv[]){const char *f_path = argv[1];int fd = open(f_path, O_WRONLY | O_CREAT | O_TRUNC, 0644);int ret = write(fd, "HelloWorld", 10);if (ret < 0){std::cout << "write(), with ret: " << ret << ", err_info: " << strerror(errno) << std::endl;}else {std::cout << "write(), with ret: " << ret << std::endl;}close(fd);return 0;
}

执行结果

$ LD_PRELOAD=./hook_lib.so ./main test.txt   
write(), with ret: -1, err_info: No such file or directory$ LD_PRELOAD=./hook_lib.so ./main newfile.txt
write(), with ret: 10

延时返回

这里比较简单不再做代码示例

sleep(time_s);		// 秒
usleep(time_ms);	// 微秒

动态控制异常注入

希望能从第三方位置读取配置,通过变更配置动态的对指定path注入指定的错误(码)类型。

从文件获得配置

hook逻辑

// hook_lib
void get_ctrl_var_file(std::string *path, int *eno, int *sleep_time){std::ifstream ifs("conf.txt");ifs >> *path;ifs >> *eno;ifs >> *sleep_time;ifs.close();
}extern ssize_t write (int __fd, __const void *__buf, size_t __n) {std::string epath;int eno, ehang_time;get_ctrl_var_file(&epath, &eno, &ehang_time);if (is_current_path(__fd, epath)) {errno = eno;hang_sleep(ehang_time);return -1;}return std_write(__fd, __buf, __n);
}

main函数

// main
int main(int argc, char *argv[]){const char *f_path = argv[1];int fd = open(f_path, O_WRONLY | O_CREAT | O_TRUNC, 0644);int ret = write(fd, "HelloWorld", 10);if (ret < 0){std::cout << "write(), with ret: " << ret << ", err_info: " << strerror(errno) << std::endl;}else {std::cout << "write(), with ret: " << ret << std::endl;}close(fd);return 0;
}

conf.txt

test.txt 2 1000000

执行结果

$ LD_PRELOAD=./hook_lib.so ./main test.txt   
write(), with ret: -1, err_info: No such file or directory$ LD_PRELOAD=./hook_lib.so ./main newfile.txt
write(), with ret: 10

从redis获得配置

hook逻辑

#include <hiredis/hiredis.h>
// hook_lib
void get_ctrl_var_redis(std::string *path, int *eno, int *sleep_time){redisContext *conn  = redisConnect("127.0.0.1", 6379);if(conn != NULL && conn->err){printf("connection error: %s\n",conn->errstr);return;}redisReply *reply = (redisReply*)redisCommand(conn,"get %s", "/hook/write/epath");*path = reply->str;reply = (redisReply*)redisCommand(conn,"get %s", "/hook/write/eno");*eno = std::atoi(reply->str);reply = (redisReply*)redisCommand(conn,"get %s", "/hook/write/ehang");*sleep_time = std::atoi(reply->str);freeReplyObject(reply);redisFree(conn);
}extern ssize_t write (int __fd, __const void *__buf, size_t __n) {std::string epath;int eno, ehang_time;get_ctrl_var_redis(&epath, &eno, &ehang_time);if (is_current_path(__fd, epath)) {errno = eno;hang_sleep(ehang_time);return -1;}return std_write(__fd, __buf, __n);
}

main函数

// main
int main(int argc, char *argv[]){const char *f_path = argv[1];int fd = open(f_path, O_WRONLY | O_CREAT | O_TRUNC, 0644);int ret = write(fd, "HelloWorld", 10);if (ret < 0){std::cout << "write(), with ret: " << ret << ", err_info: " << strerror(errno) << std::endl;}else {std::cout << "write(), with ret: " << ret << std::endl;}close(fd);return 0;
}

在redis中添加如下变量

set /hook/write/epath test.txt
set /hook/write/eno 5
set /hook/write/ehang 1000000

执行结果

$ LD_PRELOAD=./hook_lib.so ./main test.txt 
write(), with ret: -1, err_info: Input/output error$ LD_PRELOAD=./hook_lib.so ./main newfile.txt
write(), with ret: 10

in mac os

在mac os中需要使用其他的环境变量进行注入,简单试了下没能成功,抛砖引玉

https://stackoverflow.com/questions/34114587/dyld-library-path-dyld-insert-libraries-not-working

参考

https://blog.51cto.com/u_15703183/5464438

https://sq.sf.163.com/blog/article/173506648836333568

https://xz.aliyun.com/t/6883

https://www.cnblogs.com/wainiwann/p/3340277.html

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

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

相关文章

微信小程序记住密码,让登录解放双手

密码是用户最重要的数据&#xff0c;也是系统最需要保护的数据&#xff0c;我们在登录的时候需要用账号密码请求登录接口&#xff0c;如果用户勾选记住密码&#xff0c;那么下一次登录时&#xff0c;我们需要将账号密码回填到输入框&#xff0c;用户可以直接登录系统。我们分别…

OpenCV入门9——目标识别(车辆统计)

文章目录 图像轮廓查找轮廓绘制轮廓轮廓的面积与周长多边形逼近与凸包外接矩形项目总览【车辆统计】视频加载【车辆统计】去背景【车辆统计】形态学处理【车辆统计】逻辑处理【车辆统计】显示信息【车辆统计】 图像轮廓 查找轮廓 # -*- coding: utf-8 -*- import cv2 import n…

Threejs_09 gltf模型的引入(效果初现)

本节使用到的图片、素材、gltf文件&#xff0c;都是老陈打码的原素材 支持&#xff01;&#xff01;&#xff01;&#xff01;&#xff01;&#xff01;&#xff01;&#xff01;&#xff01;&#xff01;&#xff01;&#xff01;&#xff01;&#xff01;&#xff01;&#x…

SQL DELETE 语句:删除表中记录的语法和示例,以及 SQL SELECT TOP、LIMIT、FETCH FIRST 或 ROWNUM 子句的使用

SQL DELETE 语句 SQL DELETE 语句用于删除表中的现有记录。 DELETE 语法 DELETE FROM 表名 WHERE 条件;注意&#xff1a;在删除表中的记录时要小心&#xff01;请注意DELETE语句中的WHERE子句。WHERE子句指定应删除哪些记录。如果省略WHERE子句&#xff0c;将会删除表中的所…

ANSYS中如何手动为装配体添加接触约束教程

接触的类型&#xff1a; 在接触类型&#xff08;Type&#xff09;选项中&#xff0c;软件共提供了绑定接触&#xff08;Bonded&#xff09;、不分离接触&#xff08;No Separation&#xff09;、无摩擦接触&#xff08;Frictionless&#xff09;、粗糙接触&#xff08;Rough&a…

深入Ansible

1.什么是ansible ansible是新出现的自动化运维工具&#xff0c;基于Python开发&#xff0c;集合了众多运维工具&#xff08;puppet、chef、func、fabric&#xff09;的优点&#xff0c;实现了批量系统配置、批量程序部署、批量运行命令等功能。 ansible是基于 paramiko 开发的…

Linux操作系统使用及C高级编程-D11-D13结构体

由一批数据组合而成的结构型数据。组成结构型数据的每个数据称为结构型数据的“成员”&#xff0c;其描述了一块内存空间的大小及解释意义。 语法&#xff1a; struct 结构体名 { 结构体成员列表 }; 下图是struct的定义和使用方法&#xff0c;其中20行这种赋值方式错误&#xf…

向pycdc项目提的一个pr

向pycdc项目提的一个pr 前言 pycdc这个项目&#xff0c;我之前一直有在关注&#xff0c;之前使用他反编译python3.10项目&#xff0c;之前使用的 uncompyle6无法反编译pyhton3.10生成的pyc文件&#xff0c;但是pycdc可以&#xff0c;但是反编译效果感觉不如uncompyle6。但是版…

BGP笔记

自治系统----AS AS划分的原因&#xff1a;如果网络太大&#xff0c;路由数量进一步增加&#xff0c;路由表规模变得太大&#xff0c;会导致路由收敛速度变慢&#xff0c;设备性能消耗加大&#xff0c;且全网设备对于路由信息的认知不同&#xff0c;造成流量通讯障碍 AS号&…

Django批量插入数据及分页器

文章目录 一、批量插入数据二、分页1.分页器的思路2.用一个案例试试3.自定义分页器 一、批量插入数据 当我们需要大批量创建数据的时候&#xff0c;如果一条一条的去创建或许需要猴年马月 我们可以先试一试for循环试试 我们首先建立一个模型类来创建一个表 models.py&#xff…

腾讯云代金券怎么领取(腾讯云代金券在哪领取)

腾讯云代金券是可抵扣费用的优惠券&#xff0c;领券之后新购、续费、升级腾讯云相关云产品可以直接抵扣订单金额&#xff0c;节省购买腾讯云的费用&#xff0c;本文将详细介绍腾讯云代金券的领取方法和使用教程。 一、腾讯云代金券领取 1、新用户代金券【点此领取】 2、老用户…

【软考】文件的组织结构

目录 一、说明二、逻辑结构2.1 说明2.2 记录式文件2.2.1 说明2.2.2 顺序文件2.2.3 索引文件2.2.4 索引文件 2.3 流式文件 三、物理结构3.1 说明3.2 链接方式之隐式链接3.3 链接方式之显式链接 一、说明 1.组织结构是文件的组织形式。 2.逻辑结构为用户可见的的文件结构。 3.物理…

模拟量采集----测量输入的电压

生活中的模拟量有很多 大多都为电压信号和电流信号 今天讲如何测量输入的电压信号 由图中的黄线可知&#xff0c;该运放是采用的同相放大器中的电压跟随器 电压跟随器的特点是电压的输入和输出隔离 同相运放的输入与输出的关系是&#xff1a;输出1R2/R1 在图上对应的就是输…

大数据安全 测试

测试1、用户 hive/1.common2.hadoop.fql.comLEXIN.COM 和 nn/1.common2.hadoop.fql.com 分别对 Hive 进行查询 &#xff08;1&#xff09;HDFS 配置 vim /usr/local/fqlhadoop/hadoop/conf/core-site.xml <property><name>hadoop.proxyuser.hive.hosts</name&g…

linux基本指令以及热键

基本指令 ♥clear ♥whoami ♥who ♥pwd ♥ls指令&#xff08;重点&#xff09; ls -a&#xff1a; ls -l ♥mkdir ♥cd指令 ♥touch指令 ♥stat指令 ♥rmdir指令 && rm 指令 ♥man指令 ♥nano指令 ♥cp指令 ♥mv指令 ♥cat指令 &#x1f5e1;输出/输出重定向 &#x1…

腾讯云HAI域AI作画

目录 &#x1f433;前言&#xff1a; &#x1f680;了解高性能应用服务 HAI &#x1f47b;即插即用 轻松上手 &#x1f47b;横向对比 青出于蓝 &#x1f424;应用场景-AI作画 &#x1f424;应用场景-AI对话 &#x1f424;应用场景-算法研发 &#x1f680;使用HAI进行…

【链表的说明、方法---顺序表与链表的区别】

文章目录 前言什么是链表链表的结构带头和不带头的区别 链表的实现&#xff08;方法&#xff09;遍历链表头插法尾插法任意位置插入一个节点链表中是否包含某个数字删除链表某个节点删除链表中所有关键字key清空链表所有节点 ArrayList 和 LinkedList的区别总结 前言 什么是链…

整套数字化招采平台安全防御体系

招采平台作为数字化供应链的重要组成部分&#xff0c;需要确保招标采购过程的安全性,主体信息和交易数据信息尤为重要,通过必要的安全架构、技术和安全管理制度&#xff0c;做到事前防范、事中监管和事后审计的安全防御。 一、平台技术安全架构 1、先进的技术架构&#xff0c…

Latex学习

二 实例 1. \Delta_{w}\frac{\partial l}{\partial w_{i1}}weight:\frac{\partial l}{\partial x_{i1}} 效果如下 其中对于希腊字母的大小写来说&#xff0c;可以参考&#xff1a; 【LaTeX 语法】字母表示 ( 大写、小写、异体 希腊字母 | 粗体字母 | 花体字母 )_latex字母_韩…