在C++中使用HOOK修改sleep函数的方法

 更新时间:2025年12月15日 09:00:19   作者:koddnty  
Hook(钩子)是一种编程机制,它允许开发者在程序执行的特定点插入自定义代码,从而拦截、处理或修改原有的函数调用、消息传递或系统事件,本篇文章将以sleep函数为例子介绍如何在c++中使用hook修改系统函数,需要的朋友可以参考下

HOOK是什么

Hook(钩子)是一种编程机制,它允许开发者在程序执行的特定点插入自定义代码,从而拦截、处理或修改原有的函数调用、消息传递或系统事件。

通俗地说,Hook就像是给程序安装了一个“监听器”或“拦截器”。当目标函数被调用时,控制权会先转移到你的Hook代码,你可以在执行原有操作之前或之后插入自定义逻辑,甚至完全替换原有行为。例如游戏外 挂通过hook来对游戏运行时用到的函数或其他API进行修改来实现外 挂的功能。同时,hook也常与协程搭配使用,修改系统函数来为类似sleep等阻塞线程的函数添加协程的功能。

如何使用hook来修改sleep函数

下面的代码是最简单的hook的实现

#include <iostream>
#include <unistd.h>

extern "C" unsigned int sleep(unsigned int seconds)
{
    std::cout << "我们成功修改了系统提供的sleep函数!" << std::endl;
    return 0;
}

void test1()
{
    std::cout << "使用sleep函数睡2s"<< std::endl;
    sleep(2);
    std::cout << "sleep函数睡完了"<< std::endl;
}

调用函数test1(),程序运行结果如下:

使用sleep函数睡2s
我们成功修改了系统提供的sleep函数!
sleep函数睡完了

在上面的代码中,我们仅做了两件事

  • 实现一个sleep函数,与unistd.h中的sleep函数签名一致
  • 使用extern "C"告诉C++编译器"按C语言的方式处理这个函数"

定义一个相同签名的sleep为什么链接过程中不会产生重定义问题呢?在链接器链接过程中,函数符号有类似强弱符号之分,在动态库中的函数会被新目标文件的函数替换,因此此处程序运行时会运行我们重新写的sleep函数而不是unistd.h内的。

至于加extern "C"的作用,在c++编译过程中,为了区分不同的重载函数,编译器会给同名函数加入随即字符进行区分,我们的目的是重写sleep函数,因此要确保函数名与unistd.h中相同,通过加入extern "C"来做到这一点

对上述代码的改进

上述代码存在很大的缺陷,最主要的是它失去了sleep函数最基本的功能。通常我们利用hook修改函数时,我们需要维持其原有功能。我们不可能真的去实现一个完整的sleep,但我们可以获得原sleep的函数指针

在不同的平台有不同的获取库函数指针的方法,下面时在linux平台来获取sleep函数指针的例子:

获取sleep函数指针

linux为获取库函数指针提供了特定的函数dlsym,定义在<dlfcn.h>中。其函数签名为:

void *dlsym(void *restrict handle, const char *restrict symbol);

dlsym返回值是函数指针,其第一个参数是指定查找的库,第二个参数传入函数名称。

在hook场景中,handle参数常取RTLD_NEXT,表示跳过当前库查找其他库。也就是说,当symbol传入"sleep"时,dlsym跳过当前库我们定义的sleep,找到了unistd.h定义的sleep函数,并返回其函数指针。

代码改进

有了上边提供的函数,我们可以保存原有sleep函数并给他加点"小料",代码如下:

#include <iostream>
#include <unistd.h>
#include <dlfcn.h>
using sleep_fun_type = unsigned int (*)(unsigned int seconds);

sleep_fun_type original_sleep = NULL;

extern "C" unsigned int sleep(unsigned int seconds)
{
    std::cout << "我们成功修改了系统提供的sleep函数!" << std::endl;
    return original_sleep(seconds);                             // <-----这里调用我们保存下来的原始的sleep
}

void test1()
{
    original_sleep = (sleep_fun_type)dlsym(RTLD_NEXT, "sleep"); // <-----这里获得了unistd中的sleep

    std::cout << "使用sleep函数睡2s"<< std::endl;
    sleep(2);                                                   // <-----这里调用我们自己写的sleep
    std::cout << "sleep函数睡完了"<< std::endl;
}

代码相较于开始,只做了一点改进,即保存原始sleep函数,并在我们自己定义的sleep函数中调用保存的原始sleep函数。

需注意的是,编译时应加上-ldl选项链接动态库

总结

上述代码仍有许多不完善的地方,实际过程中要检查dlsym返回值是否为NULL等问题,同时代码对初始化并不规范,可以使用下面的初始化方法(gcc编译器),也可以使用其他更兼容的方法进行初始化。

__attribute__((constructor)) void init_hook()       // gcc编译器提供,main函数运行前,库和内存初始化完成后运行
{
    // 在main函数执行前先初始化—original_sleep。
    original_sleep = (SleepFunc)dlsym(RTLD_NEXT,"sleep");
}

最后,需要注意的是,如果采用上述方法重新定义sleep,会使所有库运行的sleep函数都改变成我们自己定义的sleep,如果返回值与原sleep存在差异,可能导致一些其他的隐含问题。

以上就是在C++中使用HOOK修改sleep函数的方法的详细内容,更多关于C++ HOOK修改sleep函数的资料请关注脚本之家其它相关文章!

相关文章

  • c++代码实现tea加密算法的实例详解

    c++代码实现tea加密算法的实例详解

    这篇文章主要介绍了c++代码实现tea加密算法,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2020-04-04
  • C++通过控制台访问deepseek接口并进行对话

    C++通过控制台访问deepseek接口并进行对话

    这篇文章主要为大家详细介绍了C++如何通过控制台访问deepseek接口并进行对话,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下
    2025-02-02
  • c语言实现简易版三子棋(附完整代码)

    c语言实现简易版三子棋(附完整代码)

    大家好,本篇文章主要讲的是c语言实现简易版三子棋(附完整代码),感兴趣的同学赶快来看一看吧,对你有帮助的话记得收藏一下
    2022-01-01
  • C++代码改造为UTF-8编码问题的总结(最新推荐)

    C++代码改造为UTF-8编码问题的总结(最新推荐)

    本文总结了如何将C++程序代码改造为UTF-8编码,包括操作系统、编译器和终端等各方面的设置,在实际操作中,可以通过渐进式更新的方式,只在新的代码项目中使用UTF-8编码,避免大规模修改旧代码,感兴趣的朋友一起看看吧
    2025-02-02
  • 求斐波那契(Fibonacci)数列通项的七种实现方法

    求斐波那契(Fibonacci)数列通项的七种实现方法

    本篇文章是对求斐波那契(Fibonacci)数列通项的七种实现方法进行了详细的分析介绍,需要的朋友参考下
    2013-05-05
  • 详解C++ STL vector容器访问元素的几种方式

    详解C++ STL vector容器访问元素的几种方式

    这篇文章主要介绍了详解C++ STL vector容器访问元素的几种方式,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2021-05-05
  • 解析C++编程中的继承方面的运用

    解析C++编程中的继承方面的运用

    这篇文章主要介绍了解析C++编程中的继承方面的运用,是C++入门学习中的基础知识,需要的朋友可以参考下
    2015-09-09
  • Qt实现XML与JSON数据解析全攻略

    Qt实现XML与JSON数据解析全攻略

    XML(可扩展标记语言)和JSON(JavaScript对象表示法)是两种最常用的数据格式,分别适用于不同的场景,本文将详细介绍如何利用Qt库来高效地处理XML和JSON数据,感兴趣的可以了解下
    2025-04-04
  • C语言中队列的结构和函数接口的使用示例

    C语言中队列的结构和函数接口的使用示例

    队列只允许一端进行插入数据操作,在另一端进行删除数据操作的特殊线性表,队列具有先进先出FIFO的性质;队列可用数组和链表 的方法实现,使用链表的结构实现更优一些,因为如果使用数组节,出队列时删去首元素需要将整个数组前移,效率比较低
    2023-02-02
  • 简单介绍C++编程中派生类的析构函数

    简单介绍C++编程中派生类的析构函数

    这篇文章主要介绍了C++编程中派生类的析构函数,析构函数平时一般使用较少,需要的朋友可以参考下
    2015-09-09

最新评论