Detour工具包使用_jiangxinyu的博客-程序员秘密

技术标签: C++  dll  winapi  function  工具  api  windows  

Detour工具包作用:拦截API函数
先用一个简单的例子说明问题:

//dll.cpp
#include <windows.h>
#include "detours.h"
static LONG dwSlept = 0;
 
static VOID (WINAPI * TrueSleep)(DWORD dwMilliseconds) = Sleep;

VOID WINAPI TimedSleep(DWORD dwMilliseconds)
{
    DWORD dwBeg = GetTickCount();
    TrueSleep(dwMilliseconds/10);//改成原来的1/10
    DWORD dwEnd = GetTickCount();
    InterlockedExchangeAdd(&dwSlept, dwEnd - dwBeg);
}

BOOL WINAPI DllMain(HINSTANCE hinst, DWORD dwReason, LPVOID reserved)
{
    if (dwReason == DLL_PROCESS_ATTACH) {
        DetourTransactionBegin();
        DetourUpdateThread(GetCurrentThread());
        DetourAttach(&(PVOID&)TrueSleep, TimedSleep);
        DetourTransactionCommit();
    }
    else if (dwReason == DLL_PROCESS_DETACH) {
        DetourTransactionBegin();
        DetourUpdateThread(GetCurrentThread());
        DetourDetach(&(PVOID&)TrueSleep, TimedSleep);
        DetourTransactionCommit();
    }
    return TRUE;
}

//test.cpp
#include <windows.h>
#include <stdio.h>
#include "detours.h"
int main(int argc,char *argv[])
{
 HINSTANCE hHandle;
 
 if (argc==1)
 {
  printf("没有Hook Sleep 函数!");
  Sleep(10000);
  return 1;
 }
 hHandle=LoadLibrary("1.dll");
 if (hHandle==NULL)
 {
  printf("加载Dll 失败 %X",hHandle);
  Sleep(10000);
  return -1;
 }
 else
 {
  printf("加载Dll 成功 %X/n",hHandle);
  printf("Sleep 函数 被Hook!");
  Sleep(10000);
  FreeLibrary(hHandle);
  return 0;
 }
 
 return 0;
}
-------------------------------------------------------------------------
下面用一篇转载过来的好文章,详细介绍一下Detour的原理

 Detour开发包之API拦截技术

我们截获函数执行最直接的目的就是为函数增添功能,修改返回值,或者为调试以及性能测试加入附加的代码,或者截获函数的输入输出作研究,破解使用。通过访问源代码,我们可以轻而易举的使用重建(Rebuilding)操作系统或者应用程序的方法在它们中间插入新的功能或者做功能扩展。然而,在今天这个商业化的开发世界里,以及在只有二进制代码发布的系统中,研究人员几乎没有机会可以得到源代码。本文主要讨论Detour在Windows二进制PE文件基础上的API截获技术。对于Linux平台,作这件事情将会非常的简单,由于最初的操作系统设计者引入了LD_PRELOAD。如果你设置  LD_PRELOAD=mylib.so ,那么应用程序在载入 dll时,会先查看mylib.so的符号表,在relocation 的时候会优先使用mylib.so 里的 symbol 。假如你在mylib.so里有个printf() ,那么这个printf就会替代libc的 printf。 而在mylib.so里的这个printf可以直接访问 libc.so里的printf函数指针来获得真正的 printf的入口地址。 这样,所有的dll的API HOOK在loader加载dll的时候就已经完成,非常自然,和平台相关的部分全部交给loader去处理。

一、  Detour开发库:

  简介
Detours 是一个在x86平台上截获任意Win32函数调用的工具库。中断代码可以在运行时动态加载。Detours使用一个无条件转移指令来替换目标函数的最初几条指令,将控制流转移到一个用户提供的截获函数。而目标函数中的一些指令被保存在一个被称为“trampoline” (译注:英文意为蹦床,杂技)的函数中,在这里我觉得翻译成目标函数的部分克隆/拷贝比较贴切。这些指令包括目标函数中被替换的代码以及一个重新跳转到目标函数的无条件分支。而截获函数可以替换目标函数,或者通过执行“trampoline”函数的时候将目标函数作为子程序来调用的办法来扩展功能。
Detours是执行时被插入的。内存中的目标函数的代码不是在硬盘上被修改的,因而可以在一个很好的粒度上使得截获二进制函数的执行变得更容易。例如,一个应用程序执行时加载的 DLL中的函数过程可以被插入一段截获代码(detoured),与此同时,这个DLL还可以被其他应用程序按正常情况执行(译注:也就是按照不被截获的方式执行,因为DLL二进制文件没有被修改,所以发生截获时不会影响其他进程空间加载这个DLL)。不同于DLL的重新链接或者静态重定向, Detours库中使用的这种中断技术确保不会影响到应用程序中的方法或者系统代码对目标函数的定位。
如果其他人为了调试或者在内部使用其他系统检测手段而试图修改二进制代码,Detours将是一个可以普遍使用的开发包。据我所知,Detours是第一个可以在任意平台上将未修改的目标代码作为一个可以通过“trampoline”调用的子程序来保留的开发包。而以前的系统在逻辑上预先将截获代码放到目标代码中,而不是将原始的目标代码做为一个普通的子程序来调用。我们独特的“trampoline”设计对于扩展现有的软件的二进制代码是至关重要的。
出于使用基本的函数截获功能的目的, Detours同样提供了编辑任何DLL导入表的功能,达到向存在的二进制代码中添加任意数据节表的目的,向一个新进程或者一个已经运行着的进程中注入一个DLL。一旦向一个进程注入了DLL,这个动态库就可以截获任何Win32函数,不论它是在应用程序中或者在系统库中。
  基本原理
    
1.  WIN32进程的内存管理 
众所周知,WINDOWS NT实现了虚拟存储器,每一WIN32进程拥有4GB的虚存空间, 关于WIN32进程的虚存结构及其操作的具体细节请参阅WIN32 API手册, 以下仅指出与Detours相关的几点: 
 (1) 进程要执行的指令也放在虚存空间中 
 (2) 可以使用QueryProtectEx函数把存放指令的页面的权限更改为可读可写可执行,再改写其内容,从而修改正在运行的程序 
(3) 可以使用VirtualAllocEx从一个进程为另一正运行的进程分配虚存,再使用 QueryProtectEx函数把页面的权限更改为可读可写可执行,并把要执行的指令以二进制机器码的形式写入,从而为一个正在运行的进程注入任意的代码 。
2. 拦截WIN32 API的原理 
Detours定义了三个概念:
    (1) Target函数:要拦截的函数,通常为Windows的API。
(2) Trampoline函数:Target函数的部分复制品。因为Detours将会改写Target函数,所以先把Target函数的前5个字节复制保存好,一方面仍然保存Target函数的过程调用语义,另一方面便于以后的恢复。
(3) Detour 函数:用来替代Target函数的函数。 

Detours 在Target函数的开头加入JMP Address_of_ Detour_ Function指令(共5个字节)把对Target函数的调用引导到自己的Detour函数, 把Target函数的开头的5个字节加上JMP Address_of_ Target _ Function+5共10个字节作为Trampoline函数。请参考下面的图1和图2。

 
(图1:Detour函数的过程)
http://www.xajiahe.com/cc/crack_01.jpg
 
(图2: Detour函数的调用过程)

http://www.xajiahe.com/cc/crack_02.jpg

说明:
  目标函数:
目标函数的函数体(二进制)至少有5个字节以上。按照微软的说明文档Trampoline函数的函数体是拷贝前5个字节加一个无条件跳转指令的话(如果没有特殊处理不可分割指令的话),那么前5个字节必须是完整指令,也就是不能第5个字节和第6个字节是一条不可分割的指令,否则会造成Trampoline函数执行错误,一条完整的指令被硬性分割开来,造成程序崩溃。对于第5字节和第6个字节是不可分割指令需要调整拷贝到杂技函数(Trampoline)的字节个数,这个值可以查看目标函数的汇编代码得到。此函数是目标函数的修改版本,不能在Detour函数中直接调用,需要通过对Trampoline函数的调用来达到间接调用。
  Trampoline函数:
此函数默认分配了32个字节,函数的内容就是拷贝的目标函数的前5个字节,加上一个JMP Address_of_ Target _ Function+5指令,共10个字节。
此函数仅供您的Detour函数调用,执行完前5个字节的指令后再绝对跳转到目标函数的第6个字节继续执行原功能函数。
  Detour函数:
此函数是用户需要的截获API的一个模拟版本,调用方式,参数个数必须和目标函数相一致。如目标函数是__stdcall,则Detour函数声明也必须是 __stdcall,参数个数和类型也必须相同,否则会造成程序崩溃。此函数在程序调用目标函数的第一条指令的时候就会被调用(无条件跳转过来的),如果在此函数中想继续调用目标函数,必须调用Trampoline函数(Trampoline函数在执行完目标函数的前5个字节的指令后会无条件跳转到目标函数的5个字节后继续执行),不能再直接调用目标函数,否则将进入无穷递归(目标函数跳转到Detour函数,Detour函数又跳转到目标函数的递归,因为目标函数在内存中的前5个字节已经被修改成绝对跳转)。通过对Trampoline函数的调用后可以获取目标函数的执行结果,此特性对分析目标函数非常有用,而且可以将目标函数的输出结果进行修改后再传回给应用程序。

Detour提供了向运行中的应用程序注入Detour函数和在二进制文件基础上注入Detour函数两种方式。本章主要讨论第二种工作方式。通过Detours提供的开发包可以在二进制EXE文件中添加一个名称为 Detour的节表,如下图3所示,主要目的是实现PE加载器加载应用程序的时候会自动加载您编写的Detours DLL,在Detours Dll中的DLLMain中完成对目标函数的Detour。
 
(图3)
二、  Detours提供的截获API的相关接口

Detours的提供的API 接口可以作为一个共享DLL给外部程序调用,也可以作为一个静态Lib链接到您的程序内部。

Trampoline函数可以动态或者静态的创建,如果目标函数本身是一个链接符号,使用静态的trampoline函数将非常简单。如果目标函数不能在链接时可见,那么可以使用动态trampoline函数。

  要使用静态的trampoline函数来截获目标函数,应用程序生成trampoline的时候必须使用
DETOUR_TRAMPOLINE宏。DETOUR_TRAMPOLINE有两个输入参数:trampoline的原型和目标函数的名字。
注意,对于正确的截获模型,包括目标函数,trampoline函数,以及截获函数都必须是完全一致的调用形式,包括参数格式和调用约定。当通过 trampoline函数调用目标函数的时候拷贝正确参数是截获函数的责任。由于目标函数仅仅是截获函数的一个可调用分支(截获函数可以调用 trampoline函数也可以不调用),这种责任几乎就是一种下意识的行为。

使用相同的调用约定可以确保寄存器中的值被正确的保存,并且保证调用堆栈在截获函数调用目标函数的时候能正确的建立和销毁。

可以使用DetourFunctionWithTrampoline函数来截获目标函数。这个函数有两个参数:trampoline函数以及截获函数的指针。因为目标函数已经被加到trampoline函数中,所有不需要在参数中特别指定。

  我们可以使用DetourFunction函数来创建一个动态的trampoline函数,它包括两个参数:一个指向目标函数的指针和一个截获函数的指针。DetourFunction分配一个新的trampoline函数并将适当的截获代码插入到目标函数中去。

当目标函数不是很容易使用的时候,DetourFindFunction函数可以找到那个函数,不管它是DLL中导出的函数,或者是可以通过二进制目标函数的调试符号找到。

DetourFindFunction 接受两个参数:库的名字和函数的名字。如果DetourFindFunction函数找到了指定的函数,返回该函数的指针,否则将返回一个NULL指针。 DetourFindFunction会首先使用Win32函数LoadLibrary 和GetProcAddress来定位函数,如果函数没有在 DLL的导出表中找到,DetourFindFunction将使用ImageHlp库来搜索有效的调试符号(译注:这里的调试符号是指Windows本身提供的调试符号,需要单独安装,具体信息请参考Windows的用户诊断支持信息)。DetourFindFunction返回的函数指针可以用来传递给DetourFunction以生成一个动态的trampoline函数。

我们可以调用DetourRemoveTrampoline来去掉对一个目标函数的截获。

注意,因为Detours中的函数会修改应用程序的地址空间,请确保当加入截获函数或者去掉截获函数的时候没有其他线程在进程空间中执行,这是程序员的责任。一个简单的方法保证这个时候是单线程执行就是在加载Detours库的时候在DllMain中呼叫函数。

三、  使用Detours实现对API的截获的两种方法

建立一个MFC对话框工程,在对话框的OK按钮的单击事件中加入对MessageBoxA函数的调用,编译后的程序名称MessageBoxApp,效果如图。

http://www.xajiahe.com/cc/crack_04.jpg 
(图4)


  静态方法

建立一个Dll工程,名称为ApiHook,这里以Visual C++6.0开发环境,以截获ASCII版本的MessageBoxA函数来说明。在Dll的工程加入:

DETOUR_TRAMPOLINE(int WINAPI Real_Messagebox(HWND hWnd ,
    LPCSTR lpText,
    LPCSTR lpCaption,
UINT uType), ::MessageBoxA);

生成一个静态的MessageBoxA的Trampoline函数,在Dll工程中加入目标函数的Detour函数:

int WINAPI MessageBox_Mine( HWND hWnd ,
    LPCSTR lpText,
    LPCSTR lpCaption,
    UINT uType)
{
  CString tmp= lpText;
  tmp+=” 被Detour截获”;

  return Real_Messagebox(hWnd,tmp,lpCaption,uType);

//  return ::MessageBoxA(hWnd,tmp,lpCaption,uType);  //Error 

}

在Dll入口函数中的加载Dll事件中加入:

DetourFunctionWithTrampoline((PBYTE)Real_Messagebox, (PBYTE)MessageBox_Mine);

在Dll入口函数中的卸载Dll事件中加入:

DetourRemove((PBYTE)Real_Messagebox, (PBYTE)MessageBox_Mine);

  动态方法

建立一个Dll工程,名称为ApiHook,这里以Visual C++6.0开发环境,以截获ASCII版本的MessageBoxA函数来说明。在Dll的工程加入:

//声明MessageBoxA一样的函数原型
typedef int  (WINAPI * MessageBoxSys)( HWND hWnd ,
    LPCSTR lpText,
    LPCSTR lpCaption,
    UINT uType);

//目标函数指针
MessageBoxSys SystemMessageBox=NULL;

//Trampoline函数指针
MessageBoxSys Real_MessageBox=NULL;


在Dll工程中加入目标函数的Detour函数:

int WINAPI MessageBox_Mine( HWND hWnd ,
    LPCSTR lpText,
    LPCSTR lpCaption,
    UINT uType)
{
  CString tmp= lpText;
  tmp+=” 被Detour截获”;

  return Real_Messagebox(hWnd,tmp,lpCaption,uType);

//  return ::MessageBoxA(hWnd,tmp,lpCaption,uType);  //Error 

}

在Dll入口函数中的加载Dll事件中加入:

  SystemMessageBox=(MessageBoxSys)DetourFindFunction("user32.dll","MessageBoxA");

  if(SystemMessageBox==NULL)
  {
    return FASLE;
  }

  Real_MessageBox=(MessageBoxSys)DetourFunction((PBYTE)SystemMessageBox, (PBYTE)MessageBox_Mine);

在Dll入口函数中的卸载Dll事件中加入:

DetourRemove((PBYTE)Real_Messagebox, (PBYTE)MessageBox_Mine);

  重写二进制可执行文件
使用Detours自带的SetDll.exe重写二进制可执行文件,可以在需要截获的程序中加入一个新的Detours的PE节表。对于本文就是新建一个批处理文件调用SetDll.exe。

@echo off
if not exist MessageBoxApp.exe (
echo 请将文件解压到MessageBoxApp.exe的安装目录, 然后执行补丁程序
) else (
setdll /d:ApiHook.dll MessageBoxApp.exe
)
Pause

调用后使用depends.exe(微软VC6.0开发包的工具之一)观察MessageBoxApp.exe前后变化, 可以看到Setdll已经重写MessageBoxApp.exe
成功,加入了对ApiHook.dll的依赖关系。


      (执行SetDll.exe前)                                                       (执行SetDll.exe后)

执行SetDll.exe重写后的MessageBoxApp.exe,点击确定后可以看到结果如下:

 

至此,MessageBoxApp.exe对MessageBoxA函数的调用已经被截获,弹出的对话框内容已经明显说明这一点。

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/jiangxinyu/article/details/5385426

智能推荐

图解跨域请求、反向代理原理,对前端更友好的反向代理服务器 - Caddy_反向代理解决跨域的原理_晒兜斯的博客-程序员秘密

写在开头本文采用图文解析、结合实战的方式进行网络原理解析,帮助大家去掌握一些网络知识,并了解 Caddy 的基本使用(见下图)。本人计划在近几年将持续输出深度好文,如果对这类文章感兴趣的话,还请您点个 关注 和 赞 支持一下吧!引言大家好呀~本篇文章主要是安利一个对前端更友好的 web 服务器 Caddy,我们会介绍 Caddy 的安装使用,并通过图文来解析其原理。Caddy 是唯一一个在默认情况下自动使用 HTTPS 的 Web 服务器,可以用来完成跨域请求、反向代理、静态文件服务器、部署.

Vivado下config属性的设置_bitstream.config.configrate_山音水月的博客-程序员秘密

文章目录在XDC中设置属性在XDC中设置属性点开“Language Templates”,参考xilinx给出的范例进行设置# An example XDC with the default settingsset_property BITSTREAM.CONFIG.BPI_1ST_READ_CYCLE 1 [current_design]set_property BITSTREAM....

HTML转义字符大全_&#9;_夜幕之雨的博客-程序员秘密

spring boot thymeleaf模板需要使用ISO Latin-1字符集: &amp;amp;#09; — 制表符Horizontal tab &amp;amp;#10; — 换行Line feed &amp;amp;#13; — 回车Carriage Return &amp;amp;#32; — Space ! &amp;amp;#33; — 惊叹号Exclamation mark ” &amp;amp;#34; &amp;...

验证码生成工具类+验证码验证学习笔记_手机验证码生成工具包_天乔巴夏丶的博客-程序员秘密

验证码生成工具类+验证码验证学习笔记验证码生成工具类参考自:java图形验证码生成工具类及web页面校验验证码,万分感谢!效果图:代码如下:package com.my.demo.session.others;import javax.imageio.ImageIO;import java.awt.*;import java.awt.image.BufferedImage;im...

No codesigning identities found matching the provided provisioning profile jin_iOS--九零猴的博客-程序员秘密

今天自己独立的解决了好几个问题,很开心。第一个就是关于证书的问题:之前项目经理让我们把/user/Vicky/Library/MobileDevice/Provisioning Profiles 清空,在安装最新的证书;但是安装的过程中,总是会出现这样的error:1.No codesigning identities(i.e. XXX) found matching the

10.高并发Lua、OpenResty、redis_openresty redis lua_C--G的博客-程序员秘密

变更频率低的数据,查询频率高得数据,如何提升访问速度?数据做成静态页[商品详情页]做缓存[Redis]Lua入门Lua 是一种轻量小巧的脚本语言,用标准C语言编写并以源代码形式开放, 其设计目的是为了嵌入应用程序中,从而为应用程序提供灵活的扩展和定制功能特性支持面向过程(procedure-oriented)编程和函数式编程(functional programming);自动内存管理;只提供了一种通用类型的表(table),用它可以实现数组,哈希表,集合,对象;语言内置模式匹

随便推点

Linux下Oracle数据库监听和服务启动、关闭操作_一字码的博客-程序员秘密

一、在启动数据库之前先检查监听是否已经启动检查监听状态命令:[[email protected] ~]$ lsnrctl status如果是successfully状态说明是已经启动的,如果显示是no listener则说明监听没有启动。启动监听命令:[[email protected] ~]$ lsnrctl start启动好的状态和上面图片显示的一样就说明成功了。关闭监听命令:[[email protected] ~]$ lsnrctl stop二、用Oracl.

tan x x的matlab求解,matlab画x=tan(x)_毅燃君的博客-程序员秘密

matlab怎么解非线性方程.如tan(x)=4x/(x^2+4)equ=sym('tan(x)=4*x/(x^2+4)');x=solve(equ);&gt;&gt;xx=0再问:这只能求出一个解啊再答:还有其他解吗?matlab 求解tan(x)*x-1.0x=0:0.01:10;y=tan(x).*x-1.0;乘之前有一个点.点乘是相应元素相乘.不加点,是矩阵乘法,前一个的列数,必须等于后面...

bzoj1101: [POI2007]Zap_weixin_30894389的博客-程序员秘密

f[x]=∑gcd(i,j)=dF[x]=∑d|gcd(i,j)=n/d*m/df[x]=∑i|d u(d/i)(n/d*m/d)#include&lt;cstdio&gt;#include&lt;cstring&gt;#include&lt;cctype&gt;#include&lt;algorithm&gt;using namespace std;#defi...

R语言临床预测模型的评价指标与验证指标实战:净重新分类指数NRI(Net Reclassification Index, NRI)_Data+Science+Insight的博客-程序员秘密

R语言临床预测模型的评价指标与验证指标实战:净重新分类指数NRI(Net Classification Index, NRI)#净重新分类指数NRI#分类结局的NRI指标计算#here consider pbc dataset in survival package as an examplelibrary(nricens)library(survival)dat = pbc[1:312,]dat$sex = ifelse(dat$sex=='f'..

数据库系统原理: 封锁 (封锁粒度、封锁类型、封锁协议)_amoscykl的博客-程序员秘密

封锁封锁粒度MySQL 中提供了两种封锁粒度:行级锁以及表级锁。应该尽量只锁定需要修改的那部分数据,而不是所有的资源。锁定的数据量越少,发生锁争用的可能就越小,系统的并发程度就越高。但是加锁需要消耗资源,锁的各种操作(包括获取锁、释放锁、以及检查锁状态)都会增加系统开销。因此封锁粒度越小,系统开销就越大。在选择封锁粒度时,需要在锁开销和并发程度之间做一个权衡。封锁...

discuz源代码分析_discuz源码解析_freefis的博客-程序员秘密

第一个文件当然是分析./include/common.inc.php这个文件,这个是Discuz的核心中的核心,基本上每次操作都include到了这个文件,下面就分七段来分析这个文件:灭世狂舞网络俱乐部/X0iR/wb$X:U5LZ s灭世狂舞网络俱乐部0x"ge!RGl_DSection One:QUOTE:CODE://定义PHP一些环境灭世狂舞网络俱乐部+O

推荐文章

热门文章

相关标签