设为首页收藏本站|繁體中文

Project1

 找回密码
 注册会员
搜索
查看: 5354|回复: 15
打印 上一主题 下一主题

[已经解决] 求个对RM编辑器切换地图的Hook

 关闭 [复制链接]

Lv2.观梦者 (管理员)

八云紫的式神

梦石
0
星屑
619
在线时间
1243 小时
注册时间
2008-1-1
帖子
4282

烫烫烫

跳转到指定楼层
1
发表于 2011-6-12 08:40:03 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式

就是这货..

Hook一下..每次切换的时候先执行一下自己指定的(ruby)代码,并且能收到地图id,然后再调用它原来的.....嗯就这样
那控件是个systreeview32
rm for linux(wine)制作中,期待夏娜SAMA能实现到webrm上

Lv2.观梦者

傻♂逼

梦石
0
星屑
374
在线时间
1606 小时
注册时间
2007-3-13
帖子
6562

烫烫烫开拓者

2
发表于 2011-6-12 09:36:46 | 只看该作者
表示没有时间写了
给个思路。
先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
哎呀,蛋疼什么的最有爱了
回复

使用道具 举报

头像被屏蔽

Lv1.梦旅人 (禁止发言)

梦石
0
星屑
50
在线时间
23 小时
注册时间
2011-5-22
帖子
38
3
发表于 2011-6-12 12:48:12 | 只看该作者
提示: 作者被禁止或删除 内容自动屏蔽
回复

使用道具 举报

Lv2.观梦者

傻♂逼

梦石
0
星屑
374
在线时间
1606 小时
注册时间
2007-3-13
帖子
6562

烫烫烫开拓者

4
发表于 2011-6-12 12:50:19 | 只看该作者
顺便提示一句,systreeview32和treeview基本一样,相关消息请参阅MSDN
哎呀,蛋疼什么的最有爱了
回复

使用道具 举报

Lv1.梦旅人

彩色的银子

梦石
0
星屑
50
在线时间
190 小时
注册时间
2006-6-13
帖子
1361

贵宾

5
发表于 2011-6-12 17:14:01 | 只看该作者
必须要执行RUBY?
不如你先说说你想做什么效果呢?

点评

神思大人……鞠躬。  发表于 2011-6-12 17:41
-.-
回复

使用道具 举报

Lv2.观梦者 (管理员)

八云紫的式神

梦石
0
星屑
619
在线时间
1243 小时
注册时间
2008-1-1
帖子
4282

烫烫烫

6
 楼主| 发表于 2011-6-12 17:44:38 | 只看该作者
神思 发表于 2011-6-12 17:14
必须要执行RUBY?
不如你先说说你想做什么效果呢?

要做个能用的地图扩张...必须执行ruby的原因是我只会ruby = =不过只要能hook到 执行什么都好说了吧...反正ruby能用API挂dll,call一下等待,捕捉到消息的时候把id给return回来ruby进行处理...然后再call....
rm for linux(wine)制作中,期待夏娜SAMA能实现到webrm上
回复

使用道具 举报

Lv1.梦旅人

梦石
0
星屑
115
在线时间
953 小时
注册时间
2007-4-25
帖子
805
7
发表于 2011-6-13 09:06:22 | 只看该作者
@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)

评分

参与人数 1星屑 +600 收起 理由
zh99998 + 600 thanks

查看全部评分

[email protected]:~> repeat 1 fortune
Matz is nice, so we are nice.
回复

使用道具 举报

Lv2.观梦者 (管理员)

八云紫的式神

梦石
0
星屑
619
在线时间
1243 小时
注册时间
2008-1-1
帖子
4282

烫烫烫

8
 楼主| 发表于 2011-6-13 10:42:17 | 只看该作者
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

点评

这样似乎连RM的进程都要乱搞  发表于 2011-6-13 11:23
这样才不好弄呢  发表于 2011-6-13 11:21
rm for linux(wine)制作中,期待夏娜SAMA能实现到webrm上
回复

使用道具 举报

Lv2.观梦者

傻♂逼

梦石
0
星屑
374
在线时间
1606 小时
注册时间
2007-3-13
帖子
6562

烫烫烫开拓者

9
发表于 2011-6-13 11:14:24 | 只看该作者
setWindowLong我记得是有很猥琐的办法可以乱搞的,貌似是用SetWindowHookEx来伪注入,然后DllMain判类,最后再乱搞就可以了
哎呀,蛋疼什么的最有爱了
回复

使用道具 举报

Lv2.观梦者

花开堪折直须折

梦石
0
星屑
686
在线时间
943 小时
注册时间
2010-7-17
帖子
4963

贵宾

10
发表于 2011-6-13 17:11:45 | 只看该作者
本帖最后由 冰舞蝶恋 于 2011-6-13 17:12 编辑

妈呀…………惊现一堆大神啊咧!!算了,某小菜鸟弱弱的遁走…………唉,看到看不懂…………555!
大家好,我叫节操,有一天,我被吃了。
http://forever-dream.5d6d.com
永恒の梦制作组论坛

129993099
永恒の梦制作组QQ群
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 注册会员

本版积分规则

拿上你的纸笔,建造一个属于你的梦想世界,加入吧。
 注册会员
找回密码

站长信箱:[email protected]|手机版|小黑屋|无图版|Project1游戏制作

GMT+8, 2025-1-11 09:56

Powered by Discuz! X3.1

© 2001-2013 Comsenz Inc.

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