Project1

标题: 求个对RM编辑器切换地图的Hook [打印本页]

作者: zh99998    时间: 2011-6-12 08:40
标题: 求个对RM编辑器切换地图的Hook

就是这货..

Hook一下..每次切换的时候先执行一下自己指定的(ruby)代码,并且能收到地图id,然后再调用它原来的.....嗯就这样
那控件是个systreeview32dsu_plus_rewardpost_czw
作者: yangff    时间: 2011-6-12 09:36
表示没有时间写了
给个思路。
先FindWindow抓句柄
然后SetWindowLong抓WndProc
在WndProc里面
截取鼠标左键按下的消息
然后……
TVITEM结构体
申请内存
hItem= SendMessage(mhwnd, TVM_SELECTITEM, TVGN_CARET, 0)
mask = TVIF_TEXT + TVIF_HANDLE  这个看你要去什么信息
cchTextMax = 1023(看你申请多少内存)
pszText = 丢一个内存过去这是用来放字符串的
SendMessage(mhwnd, TVM_GETITEM, 0&, 结构体) 不解释
如果只是ID的话就是SendMessage(mhwnd, TVM_SELECTITEM, TVGN_CARET, 0)取到的那个

Public Type TVITEM
    mask As Long
    hItem As Long
    state As Long
    stateMask As Long
    pszText As Long
    cchTextMax As Long
    iImage As Long
    iSelectedImage As Long
    cChildren As Long
    lParam As Long
End Type

作者: 单走大人    时间: 2011-6-12 12:48
提示: 作者被禁止或删除 内容自动屏蔽
作者: yangff    时间: 2011-6-12 12:50
顺便提示一句,systreeview32和treeview基本一样,相关消息请参阅MSDN
作者: 神思    时间: 2011-6-12 17:14
必须要执行RUBY?
不如你先说说你想做什么效果呢?
作者: zh99998    时间: 2011-6-12 17:44
神思 发表于 2011-6-12 17:14
必须要执行RUBY?
不如你先说说你想做什么效果呢?

要做个能用的地图扩张...必须执行ruby的原因是我只会ruby = =不过只要能hook到 执行什么都好说了吧...反正ruby能用API挂dll,call一下等待,捕捉到消息的时候把id给return回来ruby进行处理...然后再call....
作者: 苏小脉    时间: 2011-6-13 09:06
@yangff
先FindWindow抓句柄
然后SetWindowLong抓WndProc

对于属于外部线程的窗口,不能直接通过 SetWindowLong 替换窗口过程,普通情况下只有调用 SetWindowLong 的线程本身创建的窗口才能被替换。换句话说就是只能线程自己替换自己的窗口过程。RM 编辑器的窗口属于外部线程,所以直接调用肯定是不行的。

在必须事件驱动架构,且不修改 RPG Maker 程序本身的情况下,这个问题大概有三种解法:

1、非局部钩子
2、DLL 注入
3、直接的代码注入

第一种方法最安全,但局限性也最大——不同类型的钩子能拦截到的消息类型也不同,对于单个窗口来说,拦截能力不如直接替换窗口过程函数。第三种方法最自由,但最危险,需要考虑的地方也最多,最麻烦。下面用的是利用 CreateRemoteThread 和 LoadLibrary 这两者参数匹配的特性实现的 DLL 注入——因为 LoadLibrary 的签名和线程过程函数的签名匹配,所以 LoadLibrary 可以直接当做线程来用。在 LoadLibrary 成功映射 DLL 到目标进程地址空间之后,我们编写的 DllMain 就会被调用,代码注入就完成了。

关键步骤:
1、编写包含了 DllMain (共享库入口点)以及窗口过程函数的 hook.dll,无需导出任何函数;
2、编写另一个用于注入的 inject.dll,找到 RPG Maker 的窗口(EnumWindows);
3、通过窗口句柄打开一个 RPG Maker 的线程句柄,并请求必要的权限(OpenProcess);
4、在 RPG Maker 的地址空间中远程分配一段内存,用来保存 `hook.dll' 这个模块名(VirtualAllocEx);
5、将 `hook.dll' 写入该段内存(WriteProcessMemory);
6、创建远程线程,执行 `LoadLibrary("hook.dll");'的过程(CreateRemoteThread);
8、等待线程结束,回收垃圾(WaitForSingleObject、VirtualFreeEx、CloseHandle)。

之所以需要 4、5 步是因为 LoadLibrary 的参数是一个字符串指针,它指向的必须是局部于目标进程(在目标进程地址空间中)的字符串,所以需要远程分配内存。

inject.c:
  1. #include <Windows.h>

  2. enum
  3. {
  4.     LIB_PATH_SIZE = 9,
  5.     BUF_SIZE = 128
  6. };

  7. #define CHECK(EXPR, MSG) do                                                 \
  8. {                                                                           \
  9.     if (!(EXPR)) {                                                          \
  10.         MessageBox(0, MSG, "", MB_OK);                                      \
  11.         return;                                                             \
  12.     }                                                                       \
  13. } while (0)


  14. BOOL CALLBACK
  15. enum_windows_proc(HWND hwnd, LPARAM lparam)
  16. {
  17.     char buf[BUF_SIZE];

  18.     GetWindowText(hwnd, buf, BUF_SIZE);
  19.     if (strstr(buf, "RPG Maker") == NULL)
  20.         return TRUE;

  21.     GetClassName(hwnd, buf, BUF_SIZE);
  22.     if (strncmp(buf, "Afx:00400000:b:00010003:00000006:", 33) != 0)
  23.         return TRUE;

  24.     *((HWND*) lparam) = hwnd;

  25.     return FALSE;
  26. }

  27. void WINAPI
  28. hook_map_tree(void)
  29. {
  30.     HMODULE kernel32;
  31.     HANDLE process;
  32.     HANDLE thread;
  33.     HWND window;
  34.     DWORD process_id;
  35.     LPTHREAD_START_ROUTINE load_library;
  36.     void* lib_path;
  37.     BOOL result;

  38.     EnumWindows(enum_windows_proc, (LPARAM) &window);
  39.     GetWindowThreadProcessId(window, &process_id);

  40.     kernel32 = GetModuleHandle("kernel32");
  41.     load_library = (LPTHREAD_START_ROUTINE)
  42.                    GetProcAddress(kernel32, "LoadLibraryA");

  43.     process = OpenProcess(PROCESS_VM_OPERATION | PROCESS_VM_WRITE |
  44.                           PROCESS_VM_OPERATION | PROCESS_CREATE_THREAD |
  45.                           PROCESS_QUERY_INFORMATION, FALSE, process_id);
  46.     CHECK(process != 0, "OpenProcess failed");

  47.     lib_path = VirtualAllocEx(process, NULL, LIB_PATH_SIZE, MEM_COMMIT,
  48.                               PAGE_READWRITE);
  49.     CHECK(lib_path != 0, "VirtualAllocEx failed");

  50.     result = WriteProcessMemory(process, lib_path, "hook.dll", LIB_PATH_SIZE,
  51.                                 NULL);
  52.     CHECK(result != 0, "WriteProcessMemory failed");

  53.     thread = CreateRemoteThread(process, NULL, 0, load_library, lib_path, 0,
  54.                                 NULL);
  55.     CHECK(thread != 0, "CreateRemoteThread failed");

  56.     WaitForSingleObject(thread, INFINITE);

  57.     VirtualFreeEx(process, lib_path, LIB_PATH_SIZE, MEM_RELEASE);
  58.     CloseHandle(thread);
  59.     CloseHandle(process);
  60. }
复制代码
导出 hook_map_tree。

hook.c:

  1. #include <windows.h>
  2. #include <Commctrl.h>
  3. #include <stdio.h>
  4. #include <string.h>

  5. WNDPROC original_proc;

  6. LRESULT CALLBACK
  7. window_proc(HWND window, UINT msg, WPARAM wparam, LPARAM lparam)
  8. {
  9.     if (msg == WM_NOTIFY) {
  10.         if (((NMHDR*) lparam)->code == TVN_SELCHANGING)
  11.         {
  12.             char buf[128];
  13.             NMTREEVIEW* tv = (NMTREEVIEW*) lparam;
  14.             sprintf(buf, "Map id:%d", tv->itemNew.lParam);
  15.             MessageBox(GetForegroundWindow(), buf, "Hook", MB_OK);
  16.         }
  17.     }
  18.     return CallWindowProc(original_proc, window, msg, wparam, lparam);
  19. }

  20. enum
  21. {
  22.     BUF_SIZE = 128
  23. };

  24. BOOL CALLBACK
  25. enum_windows_proc(HWND hwnd, LPARAM lparam)
  26. {
  27.     char buf[BUF_SIZE];

  28.     GetWindowText(hwnd, buf, BUF_SIZE);
  29.     if (strstr(buf, "RPG Maker") == NULL)
  30.         return TRUE;

  31.     GetClassName(hwnd, buf, BUF_SIZE);
  32.     if (strncmp(buf, "Afx:00400000:b:00010003:00000006:", 33) != 0)
  33.         return TRUE;

  34.     *((HWND*) lparam) = hwnd;

  35.     return FALSE;
  36. }

  37. HWND
  38. find_window(void)
  39. {
  40.     HWND window;

  41.     EnumWindows(enum_windows_proc, (LPARAM) &window);
  42.     window = FindWindowEx(window, NULL, "AfxMDIFrame80s", "");
  43.     window = FindWindowEx(window, NULL, "AfxMDIFrame80s", "");
  44.     //window = FindWindowEx(window, NULL, "SysTreeView32", "");

  45.     return window;
  46. }

  47. BOOL WINAPI
  48. DllMain(HINSTANCE instance, DWORD reason, LPVOID reserved)
  49. {
  50.     if (reason == DLL_PROCESS_ATTACH)
  51.         original_proc = (WNDPROC) SetWindowLong(find_window(), GWL_WNDPROC,
  52.                                                 (LONG) window_proc);

  53.     return TRUE;
  54. }
复制代码
Tree-View 控件在选择项改变时会发送通知消息给父窗口,所以在 hook.c 里面替换 SysTreeView32 的父窗口 AfxMDIFrame80s 的窗口过程函数,并处理 WM_NOTIFY 就行了。地图的编号保存在 NMTREEVIEW#itemNew#lParam 中。这里拦截的是 TVN_SELCHANGING 通知消息,是在选择项即将改变时发送给父窗口的,所以 NMTREEVIEW#itemNew 就描述了改变后的新选项的信息。

先保证 RM 的 SysTreeView32 已经被创建,然后就可以在 Ruby 中调用:
hook_map_tree = Win32API.new('inject', 'hook_map_tree', 'v', 'v')
hook_map_tree.call
[/code]
目前的实现是选项改变时弹出地图编号。


我这里没有 RMVX,止于测试 RMXP,在搜索窗口那里可能有所不同,适当调整一下窗口标题、窗口类名就行了。inject.c 只需要找到 RM 编辑器的主窗口(因为创建窗口的线程是统一的),但 hook.c 需要找到 SysTreeView32 的父窗口。

附上两个预编译的 DLL:
Project1.zip (40.23 KB, 下载次数: 31)
作者: zh99998    时间: 2011-6-13 10:42
thanks 不过似乎有些bug 有时候会无效
另外希望改用这种调用方式,方便在不改DLL的情况下进行扩展
  1. hook_map_tree = Win32API.new('inject', 'hook_map_tree', 'v', 'v')
  2. wait_click = Win32API.new('inject', 'wait_click', 'v', 'l')
  3. while (id = wait_click.call) != -1
  4.   p id
  5. end
复制代码
VX的类名是Afx:00400000:b:00010003:00000006:003C0C11
作者: yangff    时间: 2011-6-13 11:14
setWindowLong我记得是有很猥琐的办法可以乱搞的,貌似是用SetWindowHookEx来伪注入,然后DllMain判类,最后再乱搞就可以了
作者: 冰舞蝶恋    时间: 2011-6-13 17:11
本帖最后由 冰舞蝶恋 于 2011-6-13 17:12 编辑

妈呀…………惊现一堆大神啊咧!!算了,某小菜鸟弱弱的遁走…………唉,看到看不懂…………555!
作者: 苏小脉    时间: 2011-6-14 06:49
yangff 发表于 2011-6-13 11:14
setWindowLong我记得是有很猥琐的办法可以乱搞的,貌似是用SetWindowHookEx来伪注入,然后DllMain判类,最 ...

哦,对,这我给忘了,钩子本身就可以用来注入 DLL,比 CreateRemoteThread + LoadLibrary 的方法还简单些呢。不过钩子只能注入到链接了 user32.dll 的进程,而 CreateRemoteThread 可以适用于任何链接了 kernel32.dll 的进程,包括服务。
作者: tamashii    时间: 2011-6-14 09:56
围观神贴……


tamashii于2011-6-14 09:56补充以下内容:
围观神贴……
作者: link006007    时间: 2011-6-14 14:53
苏小脉 发表于 2011-6-13 09:06
@yangff

对于属于外部线程的窗口,不能直接通过 SetWindowLong 替换窗口过程,普通情况下只有调用 SetWind ...

哈哈   你的思路让我想起了以前某人写的穿透本机防火墙访问网络的代码
其实就是通过注入IE或者一个防火墙白名单内的程序去访问远程网络
不过现在已经不能用了 - =。。。
不知道阁下知不知道一些穿透技术:$
作者: yangff    时间: 2011-6-14 19:00
苏小脉 发表于 2011-6-14 06:49
哦,对,这我给忘了,钩子本身就可以用来注入 DLL,比 CreateRemoteThread + LoadLibrary 的方法还简单些 ...

嗯,SetWindowHookEx只对GUI和部分CUI有效
作者: 精灵使者    时间: 2011-6-18 10:46
钩子什么的肯定被杀毒软件检测到的说。
作者: uiljian    时间: 2011-6-18 11:51
本帖最后由 uiljian 于 2011-6-18 11:57 编辑

纯属骗V,耶。

紫的指导。

hook.rar

487.76 KB, 下载次数: 14






欢迎光临 Project1 (https://rpg.blue/) Powered by Discuz! X3.1