打开APP
userphoto
未登录

开通VIP,畅享免费电子书等14项超值服

开通VIP
利用Thunk技术将Win32回调函数转换为C成员函数

利用Thunk技术将Win32回调函数转换为C++成员函数收藏

    N久前, 为了解决C++类成员函数的回调, 写了一段Thunk代码, 很长时间没发现有什么            BUG, 但后来在Windows XP SP2, Windows Server 2003 SP1上一运行就崩溃,不知其因。今            得其因,如下:            数据执行保护 (DEP) 是一套软硬件技术,能够在内存上执行额外检查以帮助防止在系统上            运行恶意代码。在 Microsoft Windows XP Service Pack 2、 Microsoft Windows Server            2003 Service Pack 1 、Microsoft Windows XP Tablet PC Edition 2005 和Windows            Vista 中,由硬件和软件一起强制实施 DEP。            DEP 的主要优点是可以帮助防止数据页执行代码。通常情况下,不从默认堆和堆栈执行代码。            硬件实施 DEP 检测从这些位置运行的代码,并在发现执行情况时引发异常。软件实施 DEP            可帮助阻止恶意代码利用 Windows 中的异常处理机制进行破坏。            硬件实施 DEP 是某些 DEP 兼容处理器的功能,可以防止在已标记为数据存储区的内存区域            中执行代码。 此功能也称为非执行和执行保护。 Windows XP SP2 还包括软件实施 DEP,其            目的在于减少利用 Windows 中的例外处理机制的情况。            如何解决:            当然还没有解决,我们知道WTL也用了Thunk技术,于是,用WTl 生成一个Dialog程序,在            Microsoft Windows Server 2003 Service Pack2上,把DEP 设置成回默认的,运行一下Wtl            的 Dialog,可以跑起来,同样是用Thunk,这是什么原因呢,看ATL代码,原来在这里            result = IsProcessorFeaturePresent( 12 /*PF_NX_ENABLED*/ );            确定处理器的特性            如果result 返回True            thunkPage = VirtualAlloc(NULL,            PAGE_SIZE, MEM_COMMIT, PAGE_EXECUTE_READWRITE);            用VirtualAlloc[PAGE_EXECUTE_READWRITE] 来分配Thunk 代码的内存,            到这里问题解决了.            写下这篇文档,希望使用Thunk技术的人,在代码中加上DEP 处理.            
下面贴上Thunk的代码

Thunk.h 复制代码
/*            Autor - zhang luduo (张鲁夺)            Email - zhangluduo@163.com            MSN   - zhangluduo@msn.com            Copyright 2006-2009 zhang Luduo.            All Rights Reserved.            */            #ifndef _THUNK_H            #define _THUNK_H            //            // type redefinition            //            typedef unsigned char  u1byte; // an 8 bit unsigned character type            typedef unsigned short u2byte; // a 16 bit unsigned integer type            typedef unsigned long  u4byte; // a 32 bit unsigned integer type            typedef char           s1byte; // an 8 bit signed character type            typedef short          s2byte; // a 16 bit signed integer type            typedef long           s4byte; // a 32 bit signed integer type            typedef void*          pvoid;  // a 32 bit undefined type pointer            class Thunk            {            public:            Thunk();            virtual ~Thunk();            pvoid Thiscall(pvoid pThis, u4byte MemberFxnAddr);            pvoid Stdcall (pvoid pThis, u4byte MemberFxnAddr);            template            static u4byte GetMemberFxnAddr(T MemberFxnName)            {            union            {            T From;            u4byte To;            }	union_cast;            union_cast.From = MemberFxnName;            return union_cast.To;            }            private:            #pragma pack (push, 1)            typedef struct _BYTECODE_THISCALL            {            u1byte Mov;     // 0xB9            u4byte This;    // this pointer            u1byte Jmp;     // 0xE9            u4byte Adrr;    // relative jmp            }	BYTECODE_THISCALL, *PBYTECODE_THISCALL;            typedef struct _BYTECODE_STDCALL            {            u1byte Push[3]; // push dword ptr[esp] ;            u4byte Move;    // mov dword ptr [esp + 4], ?? ?? ?? ?? ;            u4byte This;    // this pointer            u1byte Jmp;     // 0xE9            u4byte Adrr;    // relative jmp            }	BYTECODE_STDCALL, *PBYTECODE_STDCALL;            #pragma pack (pop)            // u1byte m_BytecodeThiscall[10];            // u1byte m_BytecodeStdcall [16];            BYTECODE_THISCALL m_BytecodeThiscall;            BYTECODE_STDCALL  m_BytecodeStdcall;            Thunk* m_pThis;            };            #endif // #define _THUNK_H            


Thunk.cpp 复制代码
/*            Autor - zhang luduo (张鲁夺)            Email - zhangluduo@163.com            MSN   - zhangluduo@msn.com            Copyright 2006-2009 zhang Luduo.            All Rights Reserved.            */            //#include "StdAfx.h"            #include "Thunk.h"            #ifndef _INC_WINDOWS            #	include <windows.h>            #endif            Thunk::Thunk()            {            m_pThis = (Thunk*)VirtualAlloc(NULL, sizeof (Thunk),            MEM_COMMIT, PAGE_EXECUTE_READWRITE);            }            Thunk::~Thunk()            {            if (NULL == m_pThis)            return;            VirtualFree(m_pThis, 0, MEM_RELEASE);            }            pvoid Thunk::Thiscall(pvoid pThis, u4byte MemberFxnAddr)            {            /**            encoded machine instruction   equivalent assembly languate notation            ---------------------------   -------------------------------------            B9 ?? ?? ?? ??                mov ecx, pThis  ; load ecx with this pointer            E9 ?? ?? ?? ??                jmp target addr ; jump to target function            */            /**            #define GETBYTE(b, n) ((u1byte)(b >> ((n - 1) * 8) & 0x000000FF))            u4byte dwJmpAdr = MemberFxnAddr -            (u4byte)(&(m_pThis->m_BytecodeThiscall)) -            sizeof (m_pThis->m_BytecodeThiscall);            m_pThis->m_BytecodeThiscall[0] = 0xB9;            m_pThis->m_BytecodeThiscall[1] = GETBYTE((u4byte)pThis, 1);            m_pThis->m_BytecodeThiscall[2] = GETBYTE((u4byte)pThis, 2);            m_pThis->m_BytecodeThiscall[3] = GETBYTE((u4byte)pThis, 3);            m_pThis->m_BytecodeThiscall[4] = GETBYTE((u4byte)pThis, 4);            m_pThis->m_BytecodeThiscall[5] = 0xE9;            m_pThis->m_BytecodeThiscall[6] = GETBYTE((u4byte)dwJmpAdr, 1);            m_pThis->m_BytecodeThiscall[7] = GETBYTE((u4byte)dwJmpAdr, 2);            m_pThis->m_BytecodeThiscall[8] = GETBYTE((u4byte)dwJmpAdr, 3);            m_pThis->m_BytecodeThiscall[9] = GETBYTE((u4byte)dwJmpAdr, 4);            FlushInstructionCache(GetCurrentProcess(),            &(m_pThis->m_BytecodeThiscall),            sizeof (m_BytecodeThiscall));            return &(m_pThis->m_BytecodeThiscall);            */            m_pThis->m_BytecodeThiscall.Mov  = 0xB9;            m_pThis->m_BytecodeThiscall.This = (u4byte)pThis;            m_pThis->m_BytecodeThiscall.Jmp  = 0xE9;            m_pThis->m_BytecodeThiscall.Adrr = MemberFxnAddr -            (u4byte)(&(m_pThis->m_BytecodeThiscall)) -            sizeof (BYTECODE_THISCALL);            FlushInstructionCache(GetCurrentProcess(),            &(m_pThis->m_BytecodeThiscall),            sizeof (BYTECODE_THISCALL));            return &(m_pThis->m_BytecodeThiscall);            }            pvoid Thunk::Stdcall (pvoid pThis, u4byte MemberFxnAddr)            {            /**            encoded machine instruction   equivalent assembly languate notation            ---------------------------   -------------------------------------            FF 34 24                      push dword ptr [esp]          ; save (or duplicate)            ; the return Address into stack            C7 44 24 04 ?? ?? ?? ??       mov  dword ptr [esp+4], pThis ; overwite the old            ; return address with 'this pointer'            E9 ?? ?? ?? ??                jmp  target addr              ; jump to target function            */            /**            #define GETBYTE(b, n) ((u1byte)(b >> ((n - 1) * 8) & 0x000000FF))            u4byte dwJmpAdr = MemberFxnAddr -            (u4byte)(&(m_pThis->m_BytecodeStdcall)) -            sizeof (m_pThis->m_BytecodeStdcall);            m_pThis->m_BytecodeStdcall[ 0] = 0xFF;            m_pThis->m_BytecodeStdcall[ 1] = 0x34;            m_pThis->m_BytecodeStdcall[ 2] = 0x24;            m_pThis->m_BytecodeStdcall[ 3] = 0xC7;            m_pThis->m_BytecodeStdcall[ 4] = 0x44;            m_pThis->m_BytecodeStdcall[ 5] = 0x24;            m_pThis->m_BytecodeStdcall[ 6] = 0x04;            m_pThis->m_BytecodeStdcall[ 7] = GETBYTE((u4byte)pThis, 1);            m_pThis->m_BytecodeStdcall[ 8] = GETBYTE((u4byte)pThis, 2);            m_pThis->m_BytecodeStdcall[ 9] = GETBYTE((u4byte)pThis, 3);            m_pThis->m_BytecodeStdcall[10] = GETBYTE((u4byte)pThis, 4);            m_pThis->m_BytecodeStdcall[11] = 0xE9;            m_pThis->m_BytecodeStdcall[12] = GETBYTE((u4byte)dwJmpAdr, 1);            m_pThis->m_BytecodeStdcall[13] = GETBYTE((u4byte)dwJmpAdr, 2);            m_pThis->m_BytecodeStdcall[14] = GETBYTE((u4byte)dwJmpAdr, 3);            m_pThis->m_BytecodeStdcall[15] = GETBYTE((u4byte)dwJmpAdr, 4);            FlushInstructionCache(GetCurrentProcess(),            &(m_pThis->m_BytecodeStdcall),            sizeof (m_BytecodeStdcall));            return &(m_pThis->m_BytecodeStdcall);            */            m_pThis->m_BytecodeStdcall.Push[0] = 0xFF;            m_pThis->m_BytecodeStdcall.Push[1] = 0x34;            m_pThis->m_BytecodeStdcall.Push[2] = 0x24;            m_pThis->m_BytecodeStdcall.Move    = 0x042444C7;            m_pThis->m_BytecodeStdcall.This    = (u4byte)pThis;            m_pThis->m_BytecodeStdcall.Jmp     = 0xE9;            m_pThis->m_BytecodeStdcall.Adrr    = MemberFxnAddr -            (u4byte)(&(m_pThis->m_BytecodeStdcall)) -            sizeof (BYTECODE_STDCALL);            FlushInstructionCache(GetCurrentProcess(),            &(m_pThis->m_BytecodeStdcall),            sizeof (BYTECODE_STDCALL));            return &(m_pThis->m_BytecodeStdcall);            }            


test.cpp 复制代码
#include <windows.h>            #include <stdio.h>            #include "Thunk.h"            class T            {            public:            T() : nCount(10), m_bExit(false) { }            ~T()            {            // 等待所有线程退出, 然后再做析构操作,            // 以免因为类生命周期结束, 而线程还在访问nCount, m_bExit            m_bExit = true;            WaitForMultipleObjects(2, m_Handles, TRUE, INFINITE);            printf("destruction\n");            CloseHandle(m_Handles[0]);            CloseHandle(m_Handles[1]);            }            DWORD /* __thiscall */ ThreadProcA(LPVOID lpParameter)            {            // 在此线程函数中可以访问类的数据成员nCount !            while(nCount >= 0)            {            if(m_bExit)            break;            printf("thiscall\t%d\n", nCount);            nCount--;            Sleep(1000);            }            printf("ThreadA exit\n");            return 0;            }            DWORD __stdcall ThreadProcB(LPVOID lpParameter)            {            // 在此线程函数中可以访问类的数据成员nCount !            while(nCount >= 0)            {            if(m_bExit)            break;            printf("stdcall\t\t%d\n", nCount);            nCount--;            Sleep(1000);            }            printf("ThreadB exit\n");            return 0;            }            void testA()            {            // 将线程函数地址初始到pProc中, 然后赋与CreateThread            // 注意调用约定            void* pProc = m_PorcA.Thiscall(this, Thunk::GetMemberFxnAddr(&T::ThreadProcA));            m_Handles[0] = CreateThread(0, 0, (LPTHREAD_START_ROUTINE)pProc, 0, 0, 0);            }            void testB()            {            // 将线程函数地址初始到pProc中, 然后赋与CreateThread            // 注意调用约定            void* pProc = m_PorcB.Stdcall(this, Thunk::GetMemberFxnAddr(&T::ThreadProcB));            m_Handles[1] = CreateThread(0, 0, (LPTHREAD_START_ROUTINE)pProc, 0, 0, 0);            }            private:            int nCount;            bool m_bExit;            HANDLE m_Handles[2];            Thunk m_PorcA;            Thunk m_PorcB;            };            void main()            {            T t;            t.testA();    // 测试函数            Sleep(200);   // 因为没有线程同步操作, 为使两个线程交替输出, 所以...            t.testB();    // 测试函数            Sleep(10000); // 给给予线程允分的执行时间            }            
本站仅提供存储服务,所有内容均由用户发布,如发现有害或侵权内容,请点击举报
打开APP,阅读全文并永久保存 查看更多类似文章
猜你喜欢
类似文章
VC知识库文章
深入剖析WTL
请教个小问题, 跨线程传窗口对象指针,程序崩溃
LRESULT CALLBACK WndProc
基于Thunk技术的Windows Timer的封装
《windows核心编程系列》二十二谈谈修改导入段拦截API。
更多类似文章 >>
生活服务
热点新闻
分享 收藏 导长图 关注 下载文章
绑定账号成功
后续可登录账号畅享VIP特权!
如果VIP功能使用有故障,
可点击这里联系客服!

联系客服