zoukankan      html  css  js  c++  java
  • 【VB.NET】也谈跨进程消息钩子

    写给VB.NET程序员^_^

    老生常谈了:子类化。

          我们都知道在VB6里面可以用API函数来进行子类化,以处理自身的窗体过程;如果跨进程,这就麻烦了,由于我们的函数在我们的进程中(废话),而目标进程的窗口的消息处理函数在目标进程(还是废话),所以只能想办法把我们的代码放到对方进程中去执行——并且要告知我们的进程得到了什么消息。恐怕写汇编就有点吓人了,于是大家都写DLL,其原理就是把回调函数放到一个DLL里面注入到对方进程,DLL去修改目标窗口的默认处理函数——把消息发送给我们。

          当然也有“另类”一点的:http://www.it-berater.org/ThueDownloads/index.shtml上面有一个DLL包,其中含有一个dssubcls.dll,用它,可以轻松的完成我们的工作:就像调用一个API一样简单,而且在我们的程序中使用回调函数!呵呵,省去了自己写DLL的麻烦之后,这些好处足以吸引各位观众了吧?

          好了,VB6的代码大家可以在下载的压缩包中找到,作者提供了一个以记事本为基础的实例(在\dssubcls目录下),非常详细无需详细叙述了。关键是在VB.NET里面如何使用它——如何声明API,如何进行回调,看用来子类化的API的VB6声明先:

    Declare Function SubClass& Lib "dssubcls" (ByVal HwndSubclass&, _
                                               Optional ByVal Address& = 0, _
                                               Optional ByVal OldStyle& = 0, _
                                               Optional ByVal NewStyle& = 0, _
                                               Optional ByVal Ext& = 0, _
                                               Optional ByVal SubClass& = 0)
    转化成VB.NET的声明类似下面的样子(习惯使然,我把&展开成了As Integer):

    Declare Function SubClass Lib "dssubcls" (ByVal HwndSubclass As Integer, Optional ByVal Address As Integer = 0, Optional ByVal OldStyle As Integer = 0, Optional ByVal NewStyle As Integer = 0, Optional ByVal Ext As Integer = 0, Optional ByVal SubClass As Integer = 0) As Integer

    这不是很好嘛?问题来了,这样的声明在VB6里面可以使用Addressof function来传入第二个参数(参见你下载的源码),但是在VB.NET里面直接Addressof就不成了——我们需要委托一个回调:

    Private Delegate Function HookCallBack(ByVal wMsg As Integer, ByVal wParam As Integer, ByVal lParam As Integer) As Integer

    这个委托,对应的是以下函数:

        Private Function mCallback(ByVal wMsg As Integer, ByVal wParam As Integer, ByVal lParam As Integer) As Integer
                ‘在这里处理得到的消息

        End Function

    使用时,需要注意先实例化这个委托:

        Private fix_COCD = New HookCallBack(AddressOf mCallback)

    此时,fix_COCD就是我们的mCallback函数引用了,用更直观的观点来看,fix_COCD就是一个指向mCallback的指针,相当于VB6里面的Addressof function得到的结果,看似问题解决了,于是我们写了以下代码来搞对方的进程窗体消息:

    SubClass(Handle, fix_COCD, 0, 0, 0, 1)   '修改处理函数

    问题真是接踵而至!IDE提示变量类型不符!!事实确实如此,我们把一个HookCallBack类型当做Integer来传递,无法通过检查,那么强行转换吧?当然,你可以去试试。这时,我所做的是,修改这个API声明:

    Private Declare Function SubClass Lib "dssubcls" (ByVal HwndSubclass As Integer, Optional ByVal Address As HookCallBack = Nothing, Optional ByVal OldStyle As Integer = 0, Optional ByVal NewStyle As Integer = 0, Optional ByVal Ext As Integer = 0, Optional ByVal SubClass As Integer = 0) As Integet

    使之符合我们的调用?有点倒行逆施?并非如此,当你习惯了修改API声明之后,会发现有些事变得如此简单,有些事需要你重新认识——对于WIN32 API也是如此。

    至此,大功告成:

    较为完整的代码如下:

    Code

    用这个代码的时候,可能会碰见一些“意外情况“,例如wm_datacopy,此时,我们需要进一步去获取LPARTM所指向的结构并对其进行解析(我们要读的是对方窗口所在进程的内存,具体地址由lParam确定——实际上lParam一直是一个指针——IntPrt,但它与Integer完全就是一回事(如果你使用VB2005可能需要使用Intprt.toint32或intprt=new intprt(integer)这些):

    Code

    这个类提供了Readmsg方法来读取一些内容——但这并不是完整的,我们知道,LPARAM指向的结构是这样的:

    <StructLayout(LayoutKind.Sequential)> _
    Public Structure COPYDATASTRUCT
        Public dwData As Integer
        Public cbData As Integer
        Public lpData As IntPtr
    End Structure

    其中dwData我们不是很关心,当然其中也可能存在一些有用信息(这里不想多说,网上有些文章纯属误导)

    而cbData是一个长度:lpData的长度

    lpData这里被声明为指针,看起来更直观了——它就是地址

    有了地址和长度,如何读取代码就自己写吧。

    提示一下:参考我重载的ReadProcessMemory可能对你有不少帮助。

    当然,上面提到的只是“特殊情况”中的一个典型,还有很多时候,进程是用自定义消息(>&H40A)来传递数据的,例如我所开发的这个工程,打印mCallBack的参数后,得到的是如下结果(十六进制,只提取了有用的信息):

    473  14  42257D0

    其中lParam就是一个指针,我读了其中的一部分:

        Function readmsg(ByVal address As Integer) As Byte()
            Dim buf(19) As Byte
            ReadProcessMemory(hProc, address, buf, 20, 0)
            Return buf
        End Function

    现在就明白为什么上面的代码是那样了:)

    然后进行了一个处理,得到了我想要的信息:

        '消息解码后得到的移动棋子信息:玩家,起X,起Y,止X,止Y,棋子编号,走棋总步数
        Event Move(ByVal player As Byte, ByVal sx As Byte, ByVal sy As Byte, ByVal dx As Byte, ByVal dy As Byte, ByVal name As Byte, ByVal [step] As Byte)
        Private Function mCallback(ByVal wMsg As Integer, ByVal wParam As Integer, ByVal lParam As Integer) As Integer
            If wParam = &H14 Then
                Dim s As Byte() = msg.readmsg(lParam)
                RaiseEvent Move(s(1), s(10), s(11), s(12), s(13), s(14), s(16))
            End If
        End Function

    当然,在我的工程里面重载的ReadProcessMemory并没有被使用。

    补充一下咯:

    在VB.NET中,处理自己的窗体的消息只需要重载窗体消息处理过程就可以了,无需子类化:)

    有补充一下:

    对于wm_datacopy来说,还有一些数据获取的问题没有说清楚,实际上都可以用一些方法来解决。

    真的要回家咯。饿。。贴一个重载可能更说明问题:

    Code
  • 相关阅读:
    11.分类与监督学习,朴素贝叶斯分类算法
    9、主成分分析
    7.逻辑回归实践
    8、特征选择
    6.逻辑归回
    5.线性回归算法
    6.10第十四次作业
    6.2第十三次作业
    5.27第十二次作业
    5.20第十一次作业
  • 原文地址:https://www.cnblogs.com/zcsor/p/1509331.html
Copyright © 2011-2022 走看看