城之影 發表於 2025-12-15 09:00:19

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

<div id="navCategory"><h5 class="catalogue">目录</h5><ul class="first_class_ul"><li>HOOK是什么</li><li>如何使用hook来修改sleep函数</li><ul class="second_class_ul"><li>下面的代码是最简单的hook的实现</li><li>对上述代码的改进</li><ul class="third_class_ul"><li>获取sleep函数指针</li><li>代码改进</li></ul></ul><li>总结</li><ul class="second_class_ul"></ul></ul></div><p class="maodian"></p><h2>HOOK是什么</h2>
<p>Hook(钩子)是一种编程机制,它允许开发者在程序执行的特定点插入自定义代码,从而拦截、处理或修改原有的函数调用、消息传递或系统事件。</p>
<p>通俗地说,Hook就像是给程序安装了一个&ldquo;监听器&rdquo;或&ldquo;拦截器&rdquo;。当目标函数被调用时,控制权会先转移到你的Hook代码,你可以在执行原有操作之前或之后插入自定义逻辑,甚至完全替换原有行为。例如游戏外 挂通过hook来对游戏运行时用到的函数或其他API进行修改来实现外 挂的功能。同时,hook也常与协程搭配使用,修改系统函数来为类似sleep等阻塞线程的函数添加协程的功能。</p>
<p class="maodian"></p><h2>如何使用hook来修改sleep函数</h2>
<p class="maodian"></p><h3>下面的代码是最简单的hook的实现</h3>
<div class="jb51code"><pre class="brush:cpp;">#include &lt;iostream&gt;
#include &lt;unistd.h&gt;

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

void test1()
{
    std::cout &lt;&lt; "使用sleep函数睡2s"&lt;&lt; std::endl;
    sleep(2);
    std::cout &lt;&lt; "sleep函数睡完了"&lt;&lt; std::endl;
}
</pre></div>
<p>调用函数test1(),程序运行结果如下:</p>
<div class="jb51code"><pre class="brush:cpp;">使用sleep函数睡2s
我们成功修改了系统提供的sleep函数!
sleep函数睡完了
</pre></div>
<p>在上面的代码中,我们仅做了两件事</p>
<ul><li>实现一个<code>sleep</code>函数,与unistd.h中的<code>sleep</code>函数签名一致</li><li>使用<code>extern &quot;C&quot;</code>告诉C++编译器&quot;按C语言的方式处理这个函数&quot;</li></ul>
<p>定义一个相同签名的sleep为什么链接过程中不会产生重定义问题呢?在链接器链接过程中,函数符号有类似强弱符号之分,在动态库中的函数会被新目标文件的函数<strong>替换</strong>,因此此处程序运行时会运行我们重新写的sleep函数而不是unistd.h内的。</p>
<p>至于加<code>extern &quot;C&quot;</code>的作用,在c++编译过程中,为了区分不同的重载函数,编译器会给同名函数加入随即字符进行区分,我们的目的是重写sleep函数,因此要确保函数名与unistd.h中相同,通过加入extern &quot;C&quot;来做到这一点</p>
<p class="maodian"></p><h3>对上述代码的改进</h3>
<p>上述代码存在很大的缺陷,最主要的是它失去了sleep函数最基本的功能。通常我们利用hook修改函数时,我们需要维持其原有功能。我们不可能真的去实现一个完整的sleep,但我们可以获得原sleep的函数指针</p>
<p>在不同的平台有不同的获取库函数指针的方法,下面时在linux平台来获取sleep函数指针的例子:</p>
<p class="maodian"></p><h4>获取sleep函数指针</h4>
<p>linux为获取库函数指针提供了特定的函数<code>dlsym</code>,定义在<code>&lt;dlfcn.h&gt;</code>中。其函数签名为:</p>
<div class="jb51code"><pre class="brush:cpp;">void *dlsym(void *restrict handle, const char *restrict symbol);
</pre></div>
<p><code>dlsym</code>返回值是函数指针,其第一个参数是指定查找的库,第二个参数传入函数名称。</p>
<p>在hook场景中,<code>handle</code>参数常取<code>RTLD_NEXT</code>,表示跳过当前库查找其他库。也就是说,当<code>symbol</code>传入<code>&quot;sleep&quot;</code>时,<code>dlsym</code>跳过当前库我们定义的<code>sleep</code>,找到了<code>unistd.h</code>定义的<code>sleep</code>函数,并返回其函数指针。</p>
<p class="maodian"></p><h4>代码改进</h4>
<p>有了上边提供的函数,我们可以保存原有sleep函数并给他加点&quot;小料&quot;,代码如下:</p>
<div class="jb51code"><pre class="brush:cpp;">#include &lt;iostream&gt;
#include &lt;unistd.h&gt;
#include &lt;dlfcn.h&gt;
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 &lt;&lt; "我们成功修改了系统提供的sleep函数!" &lt;&lt; std::endl;
    return original_sleep(seconds);                           // &lt;-----这里调用我们保存下来的原始的sleep
}

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

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

</pre></div>
<p>代码相较于开始,只做了一点改进,即保存原始<code>sleep</code>函数,并在我们自己定义的<code>sleep</code>函数中调用保存的原始<code>sleep</code>函数。</p>
<p>需注意的是,编译时应加上<code>-ldl</code>选项链接动态库</p>
<p class="maodian"></p><h2>总结</h2>
<p>上述代码仍有许多不完善的地方,实际过程中要检查<code>dlsym</code>返回值是否为NULL等问题,同时代码对初始化并不规范,可以使用下面的初始化方法(gcc编译器),也可以使用其他更兼容的方法进行初始化。</p>
<div class="jb51code"><pre class="brush:cpp;">__attribute__((constructor)) void init_hook()       // gcc编译器提供,main函数运行前,库和内存初始化完成后运行
{
    // 在main函数执行前先初始化—original_sleep。
    original_sleep = (SleepFunc)dlsym(RTLD_NEXT,"sleep");
}
</pre></div>
<p>最后,需要注意的是,如果采用上述方法重新定义sleep,会使所有库运行的sleep函数都改变成我们自己定义的sleep,如果返回值与原sleep存在差异,可能导致一些其他的隐含问题。</p>
<p>以上就是在C++中使用HOOK修改sleep函数的方法的详细内容,更多关于C++ HOOK修改sleep函数的资料请关注琼殿技术社区其它相关文章!</p>
                           
                            <div class="art_xg">
                              <b>您可能感兴趣的文章:</b><ul><li>浅谈c++ hook 钩子的使用介绍</li><li>C++事件处理中的__hook与__unhook用法详解</li><li>C++实现修改函数代码HOOK的封装方法</li><li>C++封装IATHOOK类实例</li><li>C++基于hook iat改变Messagebox实例</li></ul>
                            </div>

                        </div>
                        <!--endmain-->
頁: [1]
查看完整版本: 在C++中使用HOOK修改sleep函数的方法