溪流漫话

    溪流软件工作室官方博客

    正在浏览 编程技术 里的文章

    因为打算在溪流WarKey 3.0 里加入插件系统,五一的时候看了下 Lua 脚本。嗯嗯,果然挺好用。

    于是,基本上准备在使用 Lua 作为插件脚本语言了。原来考虑过的DLL形式由于安全方面不好控制,就算了。

    扩展脚本的基本格式大概如下(目前只是草稿,到时候会公布一个正式格式):

    Extension =
    {
        Application     = "xlWarKey",
        Version         = "3.0",
        NameSpace       = "
    http://www.streamlet.org/api/xlwarkey/3.0",

        ExtensionName   = "SampleExtension",

        Configuration   =
        {
            setting1    = "number",
            setting2    = "string",
            — …
        },
        Entrance      = function (id)
            Print("Hello, world");
            return false;
        end
    };

    所有内容只在一个全局变量 Extension 中定义。

    前面三项内容用作内部区分该脚本针对的应用程序及其版本,以及所需要的API等。

    ExtensionName 是该扩展的名称。

    Configuration 用于告知 溪流WarKey 所需要的设置,溪流WarKey将会在方案配置界面显示这些配置,让用户填写。允许 "number"、"string" 两种类型的配置值。脚本执行的时候,溪流WarKey会将实际值设置到这些变量上。

    Entrance 是入口函数,其中有个id参数,用于区分不同的配置。必须返回一个布尔值。目前没有定义返回值的含义。

    举个例子,喊话脚本。可能的配置的信息有:喊话的内容。这样,可以在 Configuration 里面设定一个配置项,类型为 string。溪流WarKey会让用户填写喊话内容。每个不同的喊话内容以id作区分,就是说,使用同一个脚本,用户可以配置多个喊话内容。溪流WarKey会维护这些id和配置的对应关系。最后,用户将 (喊话脚本,id=0) 这样的信息作为一个动作加入改键列表,当有对应事件发生时,溪流WarKey将调用 Entrance(id)。

    (刚才这样写下来,觉得Configuration中的每一项可能都需要一个描述信息,用于告诉用户这一项是干嘛的。这里可能会改一改。)

    此外,脚本的其他范围允许定义全局变量、全局函数等(但不推荐)。但是,只有进入Entrance后,所提供的API才会有效。目前想到的API大概有:按下某键、放开某件、移动鼠标、单击鼠标(含左键、右键、滚轮、横滚轮、侧键1、侧键2,下同)、滚动鼠标(滚轮、横滚轮)、设置剪贴板文本、取窗口大小、取光标位置、延时。如还有需要的请告知。

    上个星期终于把网站给整的差不多了,论坛、博客都已经是二级域名独立运行了,现在还剩一些内容页面。

    下一阶段计划开始开发 溪流WarKey 3.0,代码将全部重构。

    2.x版本时,因为界面复杂度比起1.2稍微有所增加,权衡后考虑使用VC6+MFC4.2来做。局限于过旧的编译器,数据结构上我没有使用STL,而是用了MFC的CList 等。可能由于不是很熟悉这套容器,某些地方有可能使用不当,宏观表现可能与有个别朋友反映的——当方案列表很长的时候出现问题——有关。虽然说,这个工具一开始是我为自己写的,但是到后来不得不顺应了一些用户的需求而加了自己不喜欢或者认为没必要加的东西。2.x的后面几个版本加的锁鼠啊之类的我自己就完全用不着。

    为了避免这种现象,我想3.0可能可以把版本分开。初步考虑了一下,大概可以分两个或者三个版本吧——

    • 首先就是经典版,或者说精简版(比赛版?),功能上延续1.2,不提供一对多改键等功能,做一个纯净的、与游戏平衡一点关系都没有的改键工具。
    • 然后是,叫什么版我还没想好(经典版?加强版?),就是拥有高级改键功能的一个版本,提供一对多,锁鼠、喊话等,基本上是2.x 的延续。
    • 最后是大而全的版本,增加一定的辅助功能,也可以加上一定的前瞻性功能,叫……豪华版?可能也不是很合适,征个名字吧~

    正好赶上最近 VS2010 发布,就用来练练手吧。界面考虑使用WTL,VS2010的MFC编出来的体积太大了。数据文件格式肯定要改变了,到时候网站提供转换功能,或者给出可下载的转换工具。

    顺便考虑一下多版本如何管理,以及那些扩展功能如何架构。前几天看了南南的U9助手,感觉他的扩展脚本的设计很不错。有些辅助功能我可能也会以插件的形式提供,这个插件没想好是用二进制代码,还是脚本语言。

    大概就这样吧。如果有功能需求可以趁此机会提出来,一般总是新版本发布后才有人来提意见希望马上改——这个时机其实并不恰到好处。一般,有任何改变,都是下一版本该进去,除非有严重bug。

    收到有些朋友的邮件,问如何检测 War3 是否处于聊天状态。

    我想这并不是什么大秘密,就在这里讲一讲。

    其实这个原理,和外挂差不多。首先,得知道 War3 内部怎么区别聊天状态和非聊天状态。有一个“外挂界”经常用的工具,叫“Cheat Engine”,它可以扫描某个进程的内存,已发现特殊数据。

    我们假定 War3 内部有这么一个变量,它为 1 表示正在聊天,为 0 表示不在聊天(事实上正是如此)。于是可以交替地让 War3 处于这两个状态,并用 Cheat Engine 搜,搜到最后会只剩下一个或者少数几个地址。然后经过观察和分析,确定某个地址是关键值。

    目前几个版本的地址如下:

    1.20、1.21:
    0×0045CB8C

    1.22:
    0×6FABDFE0

    1.23:
    0×6FAD6E30

    1.24
    0×6FAE8450

    其中,后三个的 6F 是 Game.dll 的基址。但是 DLL 的基址并不是总是固定不变的,所以实际中,可靠的方法是获取 hDll,然后加上 0×00XXXXXX。

    剩下的问题就是如何读取另一个进程的内存的问题了。Windows 提供了一系列 API 可以完成这件事。主要的几个函数是:
    OpenProcess
    ReadProcessMemory

    实现上述操作需要当前进程具有 SeDebugPrivilege 特权。使用 OpenProcessToken、LookupPrivilegeValue、AdjustTokenPrivileges 提升自身的特权级别。
    就说到这里了吧,我想对编程的朋友来说应该足够了。