ATL Thunk技术简单解析


  Thunk的基本原理是分配一段内存,然后将窗口过程设置为这段内存。这段内存的作用是将窗口过程的第一个参数(窗口句柄)替换成类的This指针,并jump到类的WinProc函数中。这样就完成了窗口过程到类的成员函数的一个转换。


ATL采用一种称为thunk的机制,简单来说,公共的窗口过程依然是类的一个静态函数,但只负责窗口的第一个消息WM_NCCREATE,其目的是在堆上开辟一小块数据区,称为thunk,这小块数据区其实是一段机器码,把这个地址换成窗口的过程函数地址,通过调用SetWindowLongPtr实现。因此以后真正的窗口过程是thunk,thunk负责把hwnd替换为对象的地址,然后jump到另一个公共的类静态函数。
  虽然是一个静态函数,不含有this指针,但是堆栈上的第一个参数已经被替换为对象的地址,用得到的对象地址就可以调用真正的消息处理函数,根据消息映射宏调用对应消息的处理过程。


  自己搞thunk的时候要主要dep保护啊。fuck


请参考

 http://www.cnblogs.com/georgepei/archive/2012/03/30/2425472.html



  http://www.cnblogs.com/georgepei/archive/2012/03/30/2425472.html

  http://www.cnblogs.com/georgepei/archive/2012/03/30/2425472.html
  http://www.cnblogs.com/georgepei/archive/2012/03/30/2425472.html

/




#include "stdafx.h"
#include <Windows.h>
#include <assert.h>

#pragma pack(push, 1)

struct tagThunk
{
	DWORD	m_mov;
	DWORD	m_this;
	BYTE	m_jmp;
	DWORD	m_relproc;// relative jmp

	BOOL Init(DWORD proc, void* pThis)
	{
		//mov [esp+4] pThis;
		m_mov =0x042444C7;
		m_this = (DWORD)pThis; 
		
		//jmp proc
		m_jmp = 0xe9;
		
		m_relproc = (DWORD)((INT_PTR)proc- ((INT_PTR)this + sizeof(tagThunk)));		

		return FlushInstructionCache(GetCurrentProcess(), this, sizeof(tagThunk));
	}

	void* GetProcAddress(){
		return this;
	}
};

#pragma  pack(pop)


int s_this;

class ThunkTest
{
public:
	int i;
	tagThunk m_thunk;
	//m_thunk位置不影响?, ATL是排在第一个
	int j;
	
	ThunkTest(){
		s_this = (int)this;
		i = 1;
		j = 2;
	}

	static void static_fun(int noneParamter, int neededParameter)
	{
		assert(s_this == noneParamter);

		ThunkTest* pThis = (ThunkTest*)noneParamter;
		pThis->member_fun(neededParameter);
	}

	void member_fun(int neededParameter)
	{

	}

	void test(int neededParameter)
	{
		m_thunk.Init((DWORD)static_fun, this);
		void* proc = m_thunk.GetProcAddress();
		_asm
		{				
			push neededParameter
			push 0	//noneParameter
			
			//这样在调用thunk的时候会把 esp+4 替换为 this
			mov edx, proc
			call edx

			add esp, 8
		}
	}
};


#include <new>

int WINAPI _tWinMain(HINSTANCE hInstance, HINSTANCE /*hPrevInstance*/, LPTSTR lpstrCmdLine, int nCmdShow)
{
	int bytes = sizeof(ThunkTest) + sizeof(int);

	//DEP问题
	LPVOID ptr = VirtualAlloc(NULL, bytes, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
	
	ThunkTest* pTest = new (ptr) ThunkTest;

	pTest->test(123);
	
	pTest->~ThunkTest();

	VirtualFree(ptr, bytes, MEM_DECOMMIT);

	return 0;
}

<span style="font-family: Arial, Helvetica, sans-serif;">
</span>
<span style="font-family: Arial, Helvetica, sans-serif;">
</span>
<span style="font-family: Arial, Helvetica, sans-serif;">参考:</span>
<span style="font-family: Arial, Helvetica, sans-serif;">http://www.codeproject.com/Articles/25198/Generic-Thunk-with-5-combinations-of-Calling-Conve</span>


2. destination of transfer instruction

Destinations of many transfer instructions are specified by OFFSET TO the source

for example :
when CPU executes the instruction at 0xFF000000 , the instruction like this :

0xFF000000 : 0xE9 0x33 0x55 0x77 0x99
0xFF000005 : ...;


0xE9 is a JMP instruction and the following 4 bytes will be interpreted as OFFSET
offset = 0x99775533 (on Intel x86 ,the lower byte stored on lower address) = -1720232653
source (src) = 0xFF000000 (the address of JMP instruction ) = 4278190080
destination (dst) = src+offset+5 ( 1 byte,JMP,4 bytes offset ) = 4278190080 – 1720232653 +5 = 2557957432 = 0x98775538

so after the instruction “ JMP -1720232653 “ the next instruction to be executed will be at :

0x98775538 : ...;

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值