龙马谷

 找回密码
 立即注册

QQ登录

只需一步,快速开始

龙马谷VIP会员办理客服QQ:82926983(如果临时会话没有收到回复,请先加QQ好友再发。)
1 [已完结] GG修改器新手入门与实战教程 31课 2 [已完结] GG修改器美化修改教程 6课 3 [已完结] GG修改器Lua脚本新手入门教程 12课
4 [已完结] 触动精灵脚本新手入门必学教程 22课 5 [已完结] 手游自动化脚本入门实战教程 9课 6 [已完结] C++射击游戏方框骨骼透视与自瞄教程 27课
7 [已完结] C++零基础UE4逆向开发FPS透视自瞄教程 29课 8 [已完结] C++零基础大漠模拟器手游自动化辅助教程 22课
以下是天马阁VIP教程,本站与天马阁合作,赞助VIP可以获得天马阁对应VIP会员,名额有限! 点击进入天马阁论坛
1 [已完结] x64CE与x64dbg入门基础教程 7课 2 [已完结] x64汇编语言基础教程 16课 3 [已完结] x64辅助入门基础教程 9课
4 [已完结] C++x64内存辅助实战技术教程 149课 5 [已完结] C++x64内存检测与过检测技术教程 10课 6 [已完结] C+x64二叉树分析遍历与LUA自动登陆教程 19课
7 [已完结] C++BT功能原理与x64实战教程 29课 8 [已完结] C+FPS框透视与自瞄x64实现原理及防护思路
查看: 1422|回复: 0

[C++源码] C++ x86 x64 类内类外子类化

[复制链接]

15

主题

7

回帖

27

积分

编程入门

Rank: 1

龙马币
48
百鬼夜行天 | 显示全部楼层 |阅读模式

C++ x86 x64 类内类外子类化

下面这里是调用例子,例子演示了类内和类外的调用。
  1. class test
  2. {
  3. public:
  4.         LRESULT WINAPI testProc(LPSUBCLASSSTRUCT pData, UINT message, WPARAM wParam, LPARAM lParam)
  5.         {
  6.                 // 跟正常的子类化差不多, 只不过第一个参数不是窗口句柄, 而是一个结构指针
  7.                 return CallWindowProcW(pData->oldProc, pData->hWnd, message, wParam, lParam);
  8.         }
  9.         LRESULT WINAPI RegisterClassWndProc(LPSUBCLASSSTRUCT pData, UINT message, WPARAM wParam, LPARAM lParam)
  10.         {
  11.                 // 跟正常的子类化差不多, 只不过第一个参数不是窗口句柄, 而是一个结构指针
  12.                 return DefWindowProcW(pData->hWnd, message, wParam, lParam);
  13.         }
  14. };

  15. LRESULT CALLBACK testProc(LPSUBCLASSSTRUCT pData, UINT message, WPARAM wParam, LPARAM lParam)
  16. {
  17.         // 跟正常的子类化差不多, 只不过第一个参数不是窗口句柄, 而是一个结构指针
  18.         return CallWindowProcW(pData->oldProc, pData->hWnd, message, wParam, lParam);
  19. }

  20. LRESULT CALLBACK RegisterClassWndProc(LPSUBCLASSSTRUCT pData, UINT message, WPARAM wParam, LPARAM lParam)
  21. {
  22.         // 跟正常的子类化差不多, 只不过第一个参数不是窗口句柄, 而是一个结构指针
  23.         return DefWindowProcW(pData->hWnd, message, wParam, lParam);
  24. }

  25. void test1(HWND hWnd)
  26. {

  27.         test a;
  28.         SubClassWindow(hWnd, &a, &test::testProc, 0);
  29.         SubClassWindow(hWnd, testProc, 0);

  30.         WNDCLASSEX wcex;
  31.         wcex.lpfnWndProc = (WNDPROC)MakeProc(RegisterClassWndProc, 0);                                // 可以这样赋值
  32.         wcex.lpfnWndProc = (WNDPROC)MakeProc(&a, &test::RegisterClassWndProc, 0);        // 也可以这样赋值

  33. }
复制代码


源码:

  1. #pragma once

  2. // 命名空间内的不建议外部使用
  3. // 使用前需要 #include<windows.h>
  4. namespace __subclass
  5. {
  6.     typedef struct tagMAKEPROCDATA
  7.     {
  8.         BYTE data[80];      // 为了通用, 指令都写到这个数组里, 也不需要1字节对齐
  9.     }MAKEPROCDATA, * LPMAKEPROCDATA;
  10. }
  11. typedef struct tagSUBCLASSSTRUCT : private __subclass::tagMAKEPROCDATA  
  12. {
  13.     // 私有继承
  14.     HWND hWnd;              // 子类化的窗口句柄
  15.     WNDPROC oldProc;        // 旧窗口过程
  16.     void* param;            // 用户数据
  17. }SUBCLASSSTRUCT, * LPSUBCLASSSTRUCT;

  18. namespace __subclass
  19. {
  20.     // 由于VirtualAlloc() 申请的字节数是一页, 一般一页是4096个字节
  21.     // 每次申请那么多, 只用几十个字节, 剩下的4000个字节都浪费了
  22.     // 所以做个简单的内存池
  23.     class simpleMempool
  24.     {
  25.     private:
  26.         struct _list
  27.         {
  28.             struct _list* next;
  29.         };
  30.         _list* ptr;
  31.         void* root;
  32.         bool init()
  33.         {
  34.             if (ptr)return false;
  35.             // 8k 个字节足够了, 最多支持 8192/sizeof(SUBCLASSSTRUCT) 次子类化操作, 在不释放的前提下
  36.             size_t size = 0x2000;   // 4k对齐
  37. #ifdef _M_X64
  38.             // x64需要自己指定申请的地址, 如果让系统自动分配, 有可能函数地址减去申请的地址会大于4个字节
  39.             INT_PTR base = 0x100000000;
  40.             for (INT_PTR i = 0; i < 50; i++)
  41.             {
  42.                 ptr = (_list*)VirtualAlloc((LPVOID)base, size,
  43.                     MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
  44.                 if (ptr)break;
  45.                 base += 0x100000000;
  46.             }
  47. #else
  48.             // x86没那么多讲究, 让系统自己分配地址
  49.             ptr = (_list*)VirtualAlloc(0, size,
  50.                 MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
  51. #endif
  52.             if (!ptr)return false;

  53.             root = ptr; // 首地址, 首节点
  54.             LPBYTE p = (LPBYTE)ptr;
  55.             size_t len = size / sizeof(SUBCLASSSTRUCT) - 1;
  56.             for (size_t i = 0; i < len; i++)
  57.             {
  58.                 // 用一个单向链表记录可用内存地址
  59.                 // 每个地址记录的大小为 SUBCLASSSTRUCT 结构大小
  60.                 p += sizeof(SUBCLASSSTRUCT);
  61.                 ptr->next = (_list*)p;
  62.                 ptr = (_list*)p;    // 指向下一个节点
  63.             }
  64.             ptr->next = 0;
  65.             ptr = (_list*)root;
  66.             return true;
  67.         }
  68.     public:
  69.         simpleMempool() :ptr(0), root(0) { ; }
  70.         ~simpleMempool() { VirtualFree(root, 0, MEM_RELEASE); ptr = 0; root = 0; }

  71.         // 申请失败则抛出int类型异常
  72.         // 异常值 1=初始化失败, 2=空间不足,需要释放一些内存
  73.         LPMAKEPROCDATA alloc()
  74.         {
  75.             size_t size = sizeof(SUBCLASSSTRUCT);
  76.             if (!ptr)init();
  77.             if (!ptr)throw int(1);
  78.             void* p = ptr;      // 每次申请都从当前节点开始取, 释放则还原到首节点
  79.             if (ptr->next == 0)throw int(2); // 没有内存了, 抛出异常

  80.             ptr = ptr->next; // 当前节点指向下一块内存
  81.             return (LPMAKEPROCDATA)p;
  82.         }
  83.         bool free(LPMAKEPROCDATA& p)
  84.         {
  85.             if (!ptr || !p)return false;
  86.             // 释放就简单的加入链表中
  87.             memset(p, 0, sizeof(SUBCLASSSTRUCT));
  88.             ((_list*)p)->next = ptr; // 放到链表头部
  89.             ptr = (_list*)p;
  90.             p = 0;
  91.             return true;
  92.         }
  93.     };
  94.     static simpleMempool mempool;
  95.     inline LPMAKEPROCDATA subclassAlloc()
  96.     {
  97.         LPMAKEPROCDATA pData = 0;
  98.         try
  99.         {
  100.             pData = mempool.alloc();
  101.         }
  102.         catch (int e)
  103.         {
  104.             switch (e)
  105.             {
  106.             case 1:
  107.                 MessageBoxW(0, L"调用SubClassWindow() 失败\n失败原因为:VirtualAlloc() 申请内存失败", L"错误", MB_OK);
  108.                 break;
  109.             case 2:
  110.                 MessageBoxW(0, L"调用SubClassWindow() 失败\n失败原因为:为SubClassWindow() 提供的内存不足", L"错误", MB_OK);
  111.                 break;
  112.             default:
  113.                 MessageBoxW(0, L"调用SubClassWindow() 失败\n失败原因为:其他未知错误", L"错误", MB_OK);
  114.                 break;
  115.             }
  116.             return 0;
  117.         }
  118.         return pData;
  119.     }
  120. }

  121. typedef LRESULT(__stdcall* SUBWNDPROC)(LPSUBCLASSSTRUCT, UINT, WPARAM, LPARAM);

  122. // 返回值强转成 WNDPROC 就可以在注册类时赋值, 窗口过程的第一个参数就是这个返回值
  123. // proc = 子类化回调函数
  124. // param = 用户数据
  125. inline LPSUBCLASSSTRUCT MakeProc(SUBWNDPROC proc, void* param)
  126. {
  127.     __subclass::LPMAKEPROCDATA pData = __subclass::subclassAlloc();
  128.     if (!pData)return 0;
  129. #ifdef _M_X64
  130.     // 这里执行的指令有45个字节, 函数地址 - 指令地址 - 指令字节数
  131.     const INT_PTR pFun = (INT_PTR)proc - INT_PTR(pData) - 45;

  132.     const unsigned char bin[] = {
  133.         0x50,                               // push rax 保存原始rax寄存器
  134.         0x48, 0xB8,0,0,0,0,0,0,0,0,         // mov rax, pData(+3) 把地址放入到eax
  135.         0x50,                               // push rax 保存地址
  136.         0x48, 0x8B, 0x40, 0x50,             // mov rax,[rax+50h] 把地址偏移0x50的值存到rax, 指向hWnd
  137.         0x48, 0xA9, 00, 00, 00, 00,         // test rax, 0   判断rax是否为0
  138.         0x58,                               // pop rax  把地址取出来放到rax
  139.         0x75, 0x04,                         // jnz 跳转4个字节
  140.         0x48, 0x89, 0x48, 0x50,             // mov [rax+50h],rcx 把原来的第一个参数存到地址+0x50的位置
  141.         0x58,                               // pop rax 还原原始rax数据
  142.         0x48, 0xb9, 0,0,0,0,0,0,0,0,        // mov rcx, pData(+32)
  143.         0xe9, 0,0,0,0                       // jmp pFun(+41)
  144.     };
  145.     memcpy(pData->data, bin, sizeof(bin));
  146.     memcpy(pData->data + 3, &pData, 8);
  147.     memcpy(pData->data + 32, &pData, 8);
  148.     memcpy(pData->data + 41, &pFun, 4);

  149. #else
  150.     // 这里执行的指令有39个字节
  151.     const int pFun = (int)proc - int(pData) - 39;
  152.     const unsigned char bin[] = {
  153.         0x50 ,                              // push eax
  154.         0xB8, 0,0,0,0,                      // mov eax, pData(+2)
  155.         0x50,                               // push eax
  156.         0x8B, 0x40, 0x50,                   // mov eax, [eax+50h]
  157.         0xA9, 0x00, 0x00, 0x00, 0x00,       // test eax,0
  158.         0x58,                               // pop eax
  159.         0x75, 0x08,                         // jne 4个字节
  160.         0xFF, 0x74, 0x24, 0x08,             // push [esp+8h]
  161.         0x8F, 0x40, 0x50,                   // pop [eax+50h]
  162.         0x58,                               // pop eax
  163.         0xc7, 0x44, 0x24, 0x04, 0,0,0,0,    // mov [esp+4], pData(+30)
  164.         0xe9, 0,0,0,0                       // jmp pFun(+35)
  165.     };
  166.     memcpy(pData->data, bin, sizeof(bin));
  167.     memcpy(pData->data + 2, &pData, 4);
  168.     memcpy(pData->data + 30, &pData, 4);
  169.     memcpy(pData->data + 35, &pFun, 4);

  170. #endif

  171.     FlushInstructionCache(GetCurrentProcess(), pData, sizeof(SUBCLASSSTRUCT));
  172.     LPSUBCLASSSTRUCT ptr = (LPSUBCLASSSTRUCT)pData;
  173.     ptr->param = param;
  174.     // 返回的这个回调函数可以用作子类化的回调函数
  175.     // 子类化第一个参数是 SUBCLASSSTRUCT 结构指针
  176.     return ptr;
  177. }

  178. // 返回值强转成 WNDPROC 就可以在注册类时赋值, 窗口过程的第一个参数就是这个返回值
  179. // pThis = 回调函数所在的类指针
  180. // proc = 子类化回调函数, 可以用 类方法, &类名::方法名 来获取
  181. // param = 用户数据
  182. template<typename T>
  183. inline LPSUBCLASSSTRUCT MakeProc(T* pThis, LRESULT(__stdcall T::* fnCallback)(LPSUBCLASSSTRUCT, UINT, WPARAM, LPARAM), void* param)
  184. {
  185.     const INT_PTR proc = (INT_PTR) * ((void**)&fnCallback);
  186.     __subclass::LPMAKEPROCDATA pData = __subclass::subclassAlloc();
  187.     if (!pData || !proc)return 0;
  188. #ifdef _M_X64
  189.     // 这里执行的指令有67个字节
  190.     const INT_PTR pFun = proc - (INT_PTR)pData - 67;
  191.     const unsigned char bin[] = {
  192.         0x50,                               // push rax 保存原始rax寄存器
  193.         0x48, 0xB8,0,0,0,0,0,0,0,0,         // mov rax, pData(+3) 把地址放入到eax
  194.         0x50,                               // push rax 保存地址
  195.         0x48, 0x8B, 0x40, 0x50,             // mov rax,[rax+50h] 把地址偏移0x50的值存到rax, 指向hWnd
  196.         0x48, 0xA9, 00, 00, 00, 00,         // test rax, 0   判断rax是否为0
  197.         0x58,                               // pop rax  把地址取出来放到rax
  198.         0x75, 0x04,                         // jnz 跳转4个字节
  199.         0x48, 0x89, 0x48, 0x50,             // mov [rax+50h],rcx 把原来的第一个参数存到地址+0x50的位置
  200.         0x58,                               // pop rax 还原原始rax数据
  201.         0x41, 0x51,                         // push r9
  202.         0x8f, 0x44, 0x24, 0x28,             // pop [esp-28h]
  203.         0x4d, 0x8b, 0xc8,                   // mov r9, r8
  204.         0x4c, 0x8b, 0xc2,                   // mov r8, rdx
  205.         0x48, 0xba, 0,0,0,0,0,0,0,0,        // mov rdx, pData(+44)
  206.         0x48, 0xb9, 0,0,0,0,0,0,0,0,        // mov rcx, pThis(+54)
  207.         0xe9, 0,0,0,0                       // jmp pFun(+63)
  208.     };
  209.     memcpy(pData->data, bin, sizeof(bin));
  210.     memcpy(pData->data + 3, &pData, 8);
  211.     memcpy(pData->data + 44, &pData, 8);
  212.     memcpy(pData->data + 54, &pThis, 8);
  213.     memcpy(pData->data + 63, &pFun, 4);

  214. #else
  215.     // 这里执行的指令有50个字节
  216.     const INT_PTR pFun = proc - (INT_PTR)pData - 50;
  217.     char as[] = {
  218.     };
  219.     const unsigned char bin[] = {
  220.         0x50 ,                              // push eax
  221.         0xB8, 0,0,0,0,                      // mov eax, pData(+2)
  222.         0x50,                               // push eax
  223.         0x8B, 0x40, 0x50,                   // mov eax, [eax+50h]
  224.         0xA9, 0x00, 0x00, 0x00, 0x00,       // test eax,0
  225.         0x58,                               // pop eax
  226.         0x75, 0x08,                         // jne 4个字节
  227.         0xFF, 0x74, 0x24, 0x08,             // push [esp+8h]
  228.         0x8F, 0x40, 0x50,                   // pop [eax+50h]
  229.         0x58,                               // pop eax
  230.         0xff, 0x34, 0x24,                   // push esp
  231.         0xc7, 0x44, 0x24, 0x04, 0,0,0,0,    // mov [esp+4], pThis(+33)
  232.         0xc7, 0x44, 0x24, 0x08, 0,0,0,0,    // mov [esp+8], pData(+41)
  233.         0xe9, 0,0,0,0                       // jmp pFun(+46)
  234.     };
  235.     memcpy(pData->data, bin, sizeof(bin));
  236.     memcpy(pData->data + 2, &pData, 4);
  237.     memcpy(pData->data + 32, &pThis, 4);
  238.     memcpy(pData->data + 41, &pData, 4);
  239.     memcpy(pData->data + 46, &pFun, 4);

  240. #endif
  241.     FlushInstructionCache(GetCurrentProcess(), pData, sizeof(SUBCLASSSTRUCT));
  242.     LPSUBCLASSSTRUCT ptr = (LPSUBCLASSSTRUCT)pData;
  243.     ptr->param = param;
  244.     // 返回的这个回调函数可以用作子类化的回调函数
  245.     // 子类化第一个参数是 SUBCLASSSTRUCT 结构指针
  246.     return ptr;
  247. }

  248. // 当不需要使用时必须要调用FreeSubClass()进行释放, 传入的数据为子类化的第一个参数
  249. // 此函数调用 SetWindowLongPtrW(hWnd, GWLP_WNDPROC, (WNDPROC)MakeProc()) 来进行子类化
  250. // 如果只需要获取子类化的函数, 请调用 MakeProc()
  251. // hWnd = 要子类化的窗口句柄
  252. // proc = 子类化回调函数
  253. // param = 用户数据
  254. inline bool __stdcall SubClassWindow(HWND hWnd, SUBWNDPROC proc, void* param)
  255. {
  256.     //if (!IsWindow(hWnd))return false;
  257.     LPSUBCLASSSTRUCT ptr = MakeProc(proc, param);
  258.     if (!ptr)return false;
  259.     ptr->hWnd = hWnd;
  260.     ptr->oldProc = (WNDPROC)SetWindowLongPtrW(hWnd, GWLP_WNDPROC, (LONG_PTR)ptr);
  261.     return true;
  262. }

  263. // 当不需要使用时必须要调用FreeSubClass()进行释放, 传入的数据为子类化的第一个参数
  264. // 此函数调用 SetWindowLongPtrW(hWnd, GWLP_WNDPROC, (WNDPROC)MakeProc()) 来进行子类化
  265. // 如果只需要获取子类化的函数, 请调用 MakeProc()
  266. // hWnd = 要子类化的窗口句柄
  267. // pThis = 回调函数所在的类指针
  268. // proc = 子类化回调函数, 可以用 类方法, &类名::方法名 来获取
  269. // param = 用户数据
  270. template<typename T>
  271. inline bool __stdcall SubClassWindow( HWND hWnd, T* pThis, LRESULT(__stdcall T::* proc)(LPSUBCLASSSTRUCT, UINT, WPARAM, LPARAM), void* param)
  272. {
  273.     if (!IsWindow(hWnd)) return false;
  274.     LPSUBCLASSSTRUCT ptr = MakeProc(pThis, proc, param);
  275.     if (!ptr)return false;
  276.     ptr->hWnd = hWnd;
  277.     ptr->oldProc = (WNDPROC)SetWindowLongPtrW(hWnd, GWLP_WNDPROC, (LONG_PTR)ptr);
  278.     // 返回的这个回调函数可以用作子类化的回调函数
  279.     // 子类化第一个参数是 SUBCLASSSTRUCT 结构指针
  280.     return true;
  281. }

  282. // 释放申请的内存空间, 调用后会把传入参数置0, 参数是子类化里第一个参数的值
  283. // 调用此函数前必须先调用 SetWindowLongW(hWnd, GWLP_WNDPROC, oldProc) 还原子类化
  284. inline bool __stdcall FreeSubClass(__subclass::LPMAKEPROCDATA& proc)
  285. {
  286.     return __subclass::mempool.free(proc);
  287. }
复制代码


简单的超类化例子

超类化可以收到 WM_NCCREATE, WM_CREATE 消息
子类化的话是收不到这两个消息的, 我感觉这个不算不常用啊
如果需要在控件创建前处理一些事情的话, 子类化实现不了, 超类化可以实现
因为子类化是针对窗口, 需要子类化的时候表示窗口已经创建成功了
超类化是基于类, 窗口还没创建时就已经处理了
  1. LRESULT CALLBACK RegisterClassWndProc(LPSUBCLASSSTRUCT pData, UINT message, WPARAM wParam, LPARAM lParam)
  2. {
  3.     // 收到所有消息都转到 pData->oldProc 这个函数去处理
  4.     // pData->oldProc 这个函数是 button 类的默认处理函数
  5.     return CallWindowProcW(pData->oldProc, pData->hWnd, message, wParam, lParam);
  6. }
  7. void test(HINSTANCE hInst)
  8. {
  9.     WNDCLASSEXW wcex = { 0 };
  10.     wcex.cbSize = sizeof(WNDCLASSEXW);
  11.     GetClassInfoExW(hInst, L"button", &wcex);   // 获取"button"类的结构
  12.     wcex.lpszClassName = L"新类名";
  13.     LPSUBCLASSSTRUCT newProc = MakeProc(RegisterClassWndProc, 0);
  14.     newProc->oldProc = wcex.lpfnWndProc;     // 保存"button"类的消息过程函数
  15.     wcex.lpfnWndProc = (WNDPROC)newProc;        // 创建类名为"新类名"时的消息过程函数
  16.     RegisterClassExW(&wcex);                    // 注册类

  17.     // 调用 CreateWindowExW 后, 这个控件的所有消息都在 RegisterClassWndProc 这个函数里
  18.     // RegisterClassWndProc 这个函数把消息都转到 button类原来的消息处理函数
  19.     CreateWindowExW(0, L"新类名", L"拥有button类的所有特征", 0, 0, 0, 0, 0, 0, 0, 0, 0);
  20. }
复制代码

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

龙马谷| C/C++辅助教程| 安卓逆向安全| 论坛导航| 免责申明|Archiver|
拒绝任何人以任何形式在本论坛发表与中华人民共和国法律相抵触的言论,本站内容均为会员发表,并不代表龙马谷立场!
任何人不得以任何方式翻录、盗版或出售本站视频,一经发现我们将追究其相关责任!
我们一直在努力成为最好的编程论坛!
Copyright© 2018-2021 All Right Reserved.
在线客服
快速回复 返回顶部 返回列表