QQ登录

只需一步,快速开始

搜索
查看: 15373|回复: 95

[C/C++] 伪任意地址HOOK类

  [复制链接]

193

积分

15

主题

2

精华

版主

Rank: 7Rank: 7Rank: 7

违规
0 点
JmPoint
8420 点
声望
17 点
赏金币
6 枚
发单信誉
0
接单信誉
0
注册时间
2015-7-25
最后登录
2018-9-10
在线时间
126 小时
发表于 2015-8-8 19:51:32 | 显示全部楼层 |阅读模式
本帖最后由 sunflover 于 2015-9-27 09:06 编辑

前言
代码HOOK技术不管在安全领域还是在木马病毒方面都运用很广泛,因为他可以改变程序执行流程,悄无声息执行我们自己的代码。

之前我也用过一些hook类,如mhook等,但很多都局限于API HOOK,有的时候无法满足实际应用。也因此,我自己的hook类就诞生了。

先贴核心代码(完整代码见附件):
[C++] 纯文本查看 复制代码
//Hook.h
#pragma once

struct t_Context
{
        DWORD EDI;
        DWORD ESI;
        DWORD EBP;
        DWORD ESP;
        DWORD EBX;
        DWORD EDX;
        DWORD ECX;
        DWORD EAX;
};

class CHook
{
public:
        CHook(void);
        ~CHook(void);
        // 添加Hook
        bool AddHook(__in DWORD dwHookAddr, __in void *pfnHookProc);
        // 移除Hook
        bool RemoveHook();
        // 寄存器结构体
        t_Context m_Context;
        // 返回地址,没有特殊需求不要修改这里
        DWORD m_dwRetAddr;
private:
        // 保存Hook地址,用于RemoveHook
        DWORD m_dwHookAddr;
        // 保存原始字节,用于RemoveHook
        BYTE m_OldCode[5];
};


[C++] 纯文本查看 复制代码
//Hook.cpp
#include "StdAfx.h"
#include "Hook.h"
#include "MyOutputDebugString.h"

//BeaEngine 反汇编引擎
#define BEA_ENGINE_STATIC
#define BEA_USE_STDCALL
#include "bea/headers/BeaEngine.h"
#pragma comment(lib, "bea/win32/lib/BeaEngine.lib")
#pragma comment(linker,"/nodefaultlib:crt.lib")
//BeaEngine end

//XEDParse 汇编引擎
#include "XEDParse/XEDParse.h"
#pragma comment(lib,"XEDParse/XEDParse.lib")

CHook::CHook(void)
{
        memset(&m_Context, 0, sizeof(m_Context));
        memset(m_OldCode, 0x90, 5);
        m_dwRetAddr = 0;
        m_dwHookAddr = 0;
}


CHook::~CHook(void)
{
}


// 添加Hook
bool CHook::AddHook(__in DWORD dwHookAddr, __in void *lpHookProc)
{
        //00240000    60              PUSHAD
        //00240001    9C              PUSHFD
        //00240002    BF 00012400     MOV EDI,0x240100
        //00240007    8BF4            MOV ESI,ESP
        //00240009    83C6 04         ADD ESI,0x4
        //0024000C    B9 20000000     MOV ECX,0x20
        //00240011    F3:A4           REP MOVS BYTE PTR ES:[EDI],BYTE PTR DS:[ESI]     ; 保存寄存器结构体,备用
        //00240013    B8 90909090     MOV EAX,0x90909090
        //00240018    FFD0            CALL EAX                                         ; 调用HookPro
        //0024001A    9D              POPFD
        //0024001B    61              POPAD
        //0024001C    90              NOP                                              ; 个数等于被毁掉的指令总长度
        //0024001D    68 90909090     PUSH 0x90909090                                  ; jmp back
        //00240022    C3              RETN
        //00240023    90              NOP
        BYTE Shell[0x100] = {0x60, 0x9C, 0xBF, 0x90, 0x90, 0x90, 0x90, 0x8B, 0xF4, 0x83, 0xC6, 0x04, 0xB9, 0x20, 0x00, 0x00, 0x00, 0xF3, 0xA4, 0xB8, 0x90, 0x90, 0x90, 0x90, 0xFF, 0xD0, 0x9D, 0x61, 0x90, 0x90};
        int pContext = (int)&m_Context;
        memcpy(&Shell[3], &pContext, 4);
        int pHookProc = (int)lpHookProc;
        memcpy(&Shell[0x14], &pHookProc, 4);

        //保存原始机器码
        DWORD dwOldProtect = 0;
        BOOL bRet = VirtualProtect((LPVOID)dwHookAddr, 0x20, PAGE_EXECUTE_READWRITE, &dwOldProtect);
        if (!bRet)
        {
                MyOutputDebugStringA("VirtualProtect(0x%p) Error!!!", dwHookAddr);
                MyOutputDebugStringA("AddHook Failed:0x%p", dwHookAddr);
                return false;
        }
        memcpy(m_OldCode, (LPVOID)dwHookAddr, 5);//用于hook还原

        DWORD dwStart = (DWORD)VirtualAlloc(NULL, 0x100, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
        if (dwStart == 0)
        {
                OutputDebugStringA("VirtualAlloc Error!!!");
                MyOutputDebugStringA("AddHook Failed:0x%p", dwHookAddr);
                return false;
        }

        //开始解析原始指令,通过反汇编引擎,识别要Hook的地址处的指令,以及破坏的指令长度
        int nSize = 0;//毁掉的指令的总字节数
        DWORD dwASM = dwStart + 30;//30为前面shell前面部分代码长度

        DISASM MyDisasm;
        memset (&MyDisasm, 0, sizeof(DISASM));
        MyDisasm.EIP = (UIntPtr)dwHookAddr;
        MyDisasm.Options = PrefixedNumeral + ShowSegmentRegs;

        while (1)
        {
                //反汇编
                int nInstLen = Disasm(&MyDisasm);
                if (nInstLen < 1)
                {
                        MyOutputDebugStringA("Disasm(%p) Error!!!", MyDisasm.EIP);
                        MyOutputDebugStringA("AddHook Failed:0x%p", dwHookAddr);
                        return false;
                }
                MyOutputDebugStringA("Addr = 0x%p nInstLen = %d %s", MyDisasm.EIP, nInstLen, MyDisasm.CompleteInstr);
                if (MyDisasm.Instruction.BranchType == RetType)
                {
                        MyOutputDebugStringA("要hook的地址开始,后面5个字节对应的指令,不能有ret");
                        MyOutputDebugStringA("AddHook Failed:0x%p", dwHookAddr);
                        return false;
                }
                nSize += nInstLen;
                //汇编,先反汇编再汇编的好处在于不用修正相对偏移地址
                XEDPARSE parse;
                memset(&parse, 0, sizeof(parse));
                parse.x64 = false;
                parse.cip = dwASM;
                memset(parse.instr, 0, 256);
                memcpy(parse.instr, MyDisasm.CompleteInstr, 64);
                XEDPARSE_STATUS status = XEDParseAssemble(&parse);
                if (status == XEDPARSE_ERROR)
                {
                        MyOutputDebugStringA("Parse Error:%s", parse.error);
                        MyOutputDebugStringA("AddHook Failed:0x%p", dwHookAddr);
                        return false;
                }
                memcpy(&Shell[dwASM - dwStart], &parse.dest[0], parse.dest_size);

                dwASM += parse.dest_size;
                MyDisasm.EIP  += nInstLen;
                if (nSize >= 5)
                {
                        m_dwRetAddr = MyDisasm.EIP;
                        m_dwHookAddr = dwHookAddr;
                        break;
                }
        }
        BYTE PushRet[6] = {0x68, 0x90, 0x90, 0x90, 0x90, 0xC3};
        memcpy(&PushRet[1], &m_dwRetAddr, 4);
        memcpy(&Shell[dwASM - dwStart], &PushRet[0], 6);
        //Shell整理好了 写过去
        memcpy((LPVOID)dwStart, Shell, 0x100);

        BYTE jmpcode[] = {0xE9, 0xFF, 0xFF, 0xFF, 0xFF}; //jmp Start
        DWORD VA =  dwStart - dwHookAddr - 5;
        memcpy(&jmpcode[1], &VA, 4);
        //hook jmp Start;
        memcpy((LPVOID)dwHookAddr, jmpcode, sizeof(jmpcode) / sizeof(jmpcode[0]));
        VirtualProtect((LPVOID)dwHookAddr, 5, dwOldProtect, &dwOldProtect);

        MyOutputDebugStringA("AddHook Success:0x%p", dwHookAddr);
        return true;
}


// 移除Hook
bool CHook::RemoveHook(void)
{
        //还原原始机器码
        if (m_dwHookAddr == 0)
        {
                return false;
        }        
        DWORD dwOldProtect = 0;
        VirtualProtect((LPVOID)m_dwHookAddr, 0x20, PAGE_EXECUTE_READWRITE, &dwOldProtect);
        memcpy((LPVOID)m_dwHookAddr, m_OldCode, 5);
        VirtualProtect((LPVOID)m_dwHookAddr, 5, dwOldProtect, &dwOldProtect);

        return true;
}


核心代码就这些了,由于使用反汇编,汇编库,实现起来代码非常简单,连注释一起也就150行代码哈,下面讲一下如何使用。
使用方法
0x00:将MyOutputDebugString.h,MyOutputDebugString.cpp,Hook.h,Hook.cpp,bea文件夹,XEDParse文件夹复制到自己项目的代码目录。
0x01:将MyOutputDebugString.h,MyOutputDebugString.cpp,Hook.h,Hook.cpp添加到项目中。
0x02:在需要使用hook的地方,#include "Hook.h",然后就可以使用了。
0x03:使用示例如下
[C++] 纯文本查看 复制代码
CHook hook1;

void MyHookPro()
{
        //hook回调函数,这里写自己的代码
}

hook1.AddHook(dwHookAddr,MyHookPro);


HookDll Demo核心代码:
[C++] 纯文本查看 复制代码
// dllmain.cpp : 定义 DLL 应用程序的入口点。
#include "stdafx.h"
#include "Hook.h"
#include "MyOutputDebugString.h"

CHook hook1;
CHook hook2;
CHook hook3;
CHook hook4;

wchar_t szTitle[] = L"Tip:Modify by sunflover";
void Hook1Proc(void)
{
        //在这里添加你的代码
        MyOutputDebugStringA("Hook1Proc EAX = 0x%.8X ECX = 0x%.8X EDX = 0x%.8X EBX = 0x%.8X ESP = 0x%.8X EBP = 0x%.8X ESI = 0x%.8X EDI = 0x%.8X",
                hook1.m_Context.EAX,hook1.m_Context.ECX,hook1.m_Context.EDX,hook1.m_Context.EBX,
                hook1.m_Context.ESP,hook1.m_Context.EBP,hook1.m_Context.ESI,hook1.m_Context.EDI);

        //$ ==>    > 00405AAE  /CALL 到 MessageBoxW 来自 HookTest.00405AA8
        //$+4      > 00120A06  |hOwner = 00120A06 ('HookDemo',class='#32770')
        //$+8      > 00550BB8  |Text = "www.jmpoep.com"
        //$+C      > 02607D40  |Title = "HookTest"
        //$+10     > 00000000  \Style = MB_OK|MB_APPLMODAL
        //这里我们改变消息框的标题吧。
        LPVOID ppTitle= (LPVOID)(hook1.m_Context.ESP +0xC);
        DWORD pszTitle = (DWORD)&szTitle[0];
        memcpy(ppTitle,&pszTitle,4);
}

void Hook2Proc(void)
{
        //在这里添加你的代码
        MyOutputDebugStringA("Hook2Proc EAX = 0x%.8X ECX = 0x%.8X EDX = 0x%.8X EBX = 0x%.8X ESP = 0x%.8X EBP = 0x%.8X ESI = 0x%.8X EDI = 0x%.8X",
                hook2.m_Context.EAX,hook2.m_Context.ECX,hook2.m_Context.EDX,hook2.m_Context.EBX,
                hook2.m_Context.ESP,hook2.m_Context.EBP,hook2.m_Context.ESI,hook2.m_Context.EDI);
}

void Hook3Proc(void)
{
        //在这里添加你的代码
        MyOutputDebugStringA("Hook3Proc EAX = 0x%.8X ECX = 0x%.8X EDX = 0x%.8X EBX = 0x%.8X ESP = 0x%.8X EBP = 0x%.8X ESI = 0x%.8X EDI = 0x%.8X",
                hook3.m_Context.EAX,hook3.m_Context.ECX,hook3.m_Context.EDX,hook3.m_Context.EBX,
                hook3.m_Context.ESP,hook3.m_Context.EBP,hook3.m_Context.ESI,hook3.m_Context.EDI);
        //这里就简单的弹出个消息框
        MessageBoxA(NULL, "Hook3Proc\tBy sunflover","www.jmpoep.com",MB_ICONINFORMATION);
}

void Hook4Proc(void)
{
        //在这里添加你的代码
        MyOutputDebugStringA("Hook3Proc EAX = 0x%.8X ECX = 0x%.8X EDX = 0x%.8X EBX = 0x%.8X ESP = 0x%.8X EBP = 0x%.8X ESI = 0x%.8X EDI = 0x%.8X",
                hook4.m_Context.EAX,hook4.m_Context.ECX,hook4.m_Context.EDX,hook4.m_Context.EBX,
                hook4.m_Context.ESP,hook4.m_Context.EBP,hook4.m_Context.ESI,hook4.m_Context.EDI);
}

//bug1:要hook的地址开始后5个字节,不会retn,如下hook 00405AC5 处会失败,hook 00405AC0成功
//00405ABD  |.  8B45 E4       mov eax,[local.7]
//00405AC0  |>  E8 C0CB0F00   call HookDemo.00502685
//00405AC5  \.  C3            retn
//00405AC6  /$  33C0          xor eax,eax
//00405AC8  |.  3945 E4       cmp [local.7],eax


//请使用Release目录的HookTest.exe测试hook

BOOL APIENTRY DllMain( HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved)
{
        switch (ul_reason_for_call)
        {
        case DLL_PROCESS_ATTACH:
                {
                        DWORD dwAddrMSGBOX = (DWORD)GetProcAddress(GetModuleHandle("user32.dll"), "MessageBoxW");
                        hook1.AddHook(dwAddrMSGBOX, Hook1Proc);
                        hook2.AddHook(0x00405AB1, Hook2Proc);
                        hook3.AddHook(0x00405AC0, Hook3Proc);
                        hook4.AddHook(0x00405AC5, Hook4Proc);//ret处hook会失败         
                }
        case DLL_THREAD_ATTACH:
        case DLL_THREAD_DETACH:
        case DLL_PROCESS_DETACH:
                break;
        }
        return TRUE;
}

关于编译器和编译选项,我在这里也有必要提一下。我使用的VS2010,编译选项如下:
(其他编译选项可能会有问题,同样,使用其他编译器编译也可能有问题。)


理论上存在的问题(也因为这点所以叫伪任意地址Hook)
0x00:要hook的地址开始后5个字节,不能有retn,如下hook 00405AC5 处会失败,hook 00405AC0成功
//00405ABD  |.  8B45 E4       mov eax,[local.7]
//00405AC0  |>  E8 C0CB0F00   call HookDemo.00502685
//00405AC5  \.  C3            retn
//00405AC6  /$  33C0          xor eax,eax
//00405AC8  |.  3945 E4       cmp [local.7],eax
完整HookDemo源代码(VS2010编译):
游客,如果您要查看本帖隐藏内容请回复
版权声明:转载请注明原帖地址。










本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?立即加入

x

评分

参与人数 8JmPoint +89 +12 收起 理由
SmLove + 1 良心贴,前排支持!!!!!
风动鸣 + 2 良心贴,前排支持!!!!!
W1nds + 20 + 2 膜拜我菊花大屌
fywy + 2 + 2 好文!!!!
卡卡西 + 2 + 2 淡定
yAYa + 2 + 2 膜拜师傅.
囚徒灬 + 20 + 2 小菜最大的权限了
拓海真一 + 40 + 2 菊花Hook大法好!!

查看全部评分

just for fun!
博客地址:http://blog.csdn.net/sunflover454/article/
回复

使用道具 举报

193

积分

15

主题

2

精华

版主

Rank: 7Rank: 7Rank: 7

违规
0 点
JmPoint
8420 点
声望
17 点
赏金币
6 枚
发单信誉
0
接单信誉
0
注册时间
2015-7-25
最后登录
2018-9-10
在线时间
126 小时
发表于 2015-8-9 20:56:32 | 显示全部楼层
lyliucn 发表于 2015-8-9 11:43
王者风范,多来点HOOK资料。

给一个详细的例子多好。

详细例子附件中有。
http://www.jmpoep.com/thread-217-1-1.html
这个帖子里第三课也有讲到,如果你不会C++,只会易语言,那我只能呵呵了。
just for fun!
博客地址:http://blog.csdn.net/sunflover454/article/
回复 支持 反对

使用道具 举报

173

积分

26

主题

1

精华

专家团

Rank: 7Rank: 7Rank: 7

违规
0 点
JmPoint
3613 点
声望
13 点
赏金币
0 枚
发单信誉
0
接单信誉
0
注册时间
2015-7-27
最后登录
2018-11-30
在线时间
254 小时
发表于 2015-8-8 20:57:32 | 显示全部楼层
尽显王者风范

点评

师傅 这次是不是水过了  详情 回复 发表于 2015-8-8 21:21
回复 支持 反对

使用道具 举报

869

积分

157

主题

8

精华

坛主

Rank: 9Rank: 9Rank: 9

违规
0 点
JmPoint
8006 点
声望
31 点
赏金币
0 枚
发单信誉
0
接单信誉
0
注册时间
2015-7-23
最后登录
2019-1-16
在线时间
765 小时

论坛元勋奖章特殊贡献勋章4st TeAm成员土豪勋章

发表于 2015-8-8 21:00:04 | 显示全部楼层
尽显王者风范
回复 支持 反对

使用道具 举报

107

积分

13

主题

0

精华

专家团

Rank: 7Rank: 7Rank: 7

违规
0 点
JmPoint
9959 点
声望
11 点
赏金币
0 枚
发单信誉
0
接单信誉
0
注册时间
2015-7-26
最后登录
2018-10-26
在线时间
252 小时
发表于 2015-8-8 21:00:43 | 显示全部楼层
膜拜向日师傅
回复 支持 反对

使用道具 举报

5

积分

1

主题

0

精华

临时会员

违规
0 点
JmPoint
293 点
声望
0 点
赏金币
0 枚
发单信誉
0
接单信誉
0
注册时间
2015-7-27
最后登录
2018-6-7
在线时间
41 小时
发表于 2015-8-8 21:01:19 | 显示全部楼层
王者,学习下。
回复 支持 反对

使用道具 举报

11

积分

2

主题

0

精华

临时会员

违规
0 点
JmPoint
941 点
声望
0 点
赏金币
8 枚
发单信誉
0
接单信誉
0
注册时间
2015-7-26
最后登录
2018-12-11
在线时间
86 小时
发表于 2015-8-8 21:02:00 | 显示全部楼层
必须支持了啊
回复 支持 反对

使用道具 举报

193

积分

15

主题

2

精华

版主

Rank: 7Rank: 7Rank: 7

违规
0 点
JmPoint
8420 点
声望
17 点
赏金币
6 枚
发单信誉
0
接单信誉
0
注册时间
2015-7-25
最后登录
2018-9-10
在线时间
126 小时
发表于 2015-8-8 21:21:14 | 显示全部楼层
LCC 发表于 2015-8-8 20:57
尽显王者风范

师傅 这次是不是水过了
just for fun!
博客地址:http://blog.csdn.net/sunflover454/article/
回复 支持 反对

使用道具 举报

58

积分

4

主题

0

精华

正式会员

Rank: 1

违规
0 点
JmPoint
71 点
声望
10 点
赏金币
0 枚
发单信誉
0
接单信誉
0
注册时间
2015-7-27
最后登录
2019-1-15
在线时间
134 小时
发表于 2015-8-8 22:09:45 | 显示全部楼层
膜拜向日师傅
回复 支持 反对

使用道具 举报

7

积分

2

主题

0

精华

临时会员

违规
0 点
JmPoint
192 点
声望
0 点
赏金币
0 枚
发单信誉
0
接单信誉
0
注册时间
2015-8-5
最后登录
2018-9-4
在线时间
26 小时
发表于 2015-8-8 22:46:09 | 显示全部楼层
HOOK已玩坏
回复 支持 反对

使用道具 举报

173

积分

26

主题

1

精华

专家团

Rank: 7Rank: 7Rank: 7

违规
0 点
JmPoint
3613 点
声望
13 点
赏金币
0 枚
发单信誉
0
接单信誉
0
注册时间
2015-7-27
最后登录
2018-11-30
在线时间
254 小时
发表于 2015-8-8 23:50:29 | 显示全部楼层
sunflover 发表于 2015-8-8 21:21
师傅 这次是不是水过了

师傅,你看下面,哈哈,都水了
回复 支持 反对

使用道具 举报

*滑动验证:
您需要登录后才可以回帖 登录 | 立即加入

本版积分规则

关闭

站长推荐上一条 /2 下一条

QQ|Archiver|手机版|小黑屋|零日安全论坛 点击这里给我发消息

GMT+8, 2019-1-18 13:08 , Processed in 0.116511 second(s), 34 queries .

Powered by Discuz! X3.4

© 2001-2013 Comsenz Inc.

快速回复 返回顶部 返回列表