VB.NET的内嵌ASM——本进程版~后面还有个本进程消息钩子的尾巴~~~~~~~~~~~~~

多年前就看过,一直没有用几次……

 

说实在的,还是那点事,只是把前面的代码稍加修改一下,用到这里。

不过提出几个小问题:

以前的代码中,没有用ZEROMEM函数来初始化那一块申请的内存,并且在清空时也没有实际清空,现在改成清空。因为在写代码时可能因疏忽导致ASM出错,在查看内存中的代码时却全七八糟的……基于同样的原因,使用了getlasterror函数,取得产生的异常: Throw New Exception("GetLastError返回值为:" & GetLastError, ex)。

另一个,应该属BUG了,原来的四字节对齐数据代码有一些问题(STR的结尾问题),当数据恰好整除4时,会连续存放(并且当时写错了,没有真的4字节对齐吧!)。

再就是用marsh的方法替代了read/write MEM的API。其他没什么了,不过发现一处问题,Marshal.ReadByte还有 Marshal.WriteByte的一个重载貌似不好使哦!哎,就是带addressoffset的那种重载。

 

已下为代码:

类:

 

Imports System.Runtime.InteropServices
''' <summary>
''' 用于运行ASM代码
''' 代码和数据被限制在4KB空间内:
''' 前512字节被用于代码,后面的用于数据
''' </summary>
''' <remarks>修改自
''' http://www.cnblogs.com/zcsor/archive/2009/03/29/1424216.html
''' http://www.cnblogs.com/zcsor/archive/2009/04/13/1434487.html%3C/remarks>
Public Class RunASMCode
    '自定义常数
    Private Const MEM_SIZE As Integer = &H1000      '申请内存大小

    Protected AllocBaseAddress As Integer           '申请内存的基地址

    Protected PtrAddressOffset As Integer           '代码基地址
    Protected ObjAddressOffset As Integer           '数据基地址

    Protected DataArraylist As New ArrayList
    <DllImport("kernel32", SetLastError:=True, EntryPoint:="RtlZeroMemory")> Public Shared Sub ZeroMemory(ByVal ptr As IntPtr, ByVal numBytes As Integer)
    End Sub
    Private Declare Function CallWindowProc Lib "User32.dll" Alias "CallWindowProcA" (ByVal lpWndFunc As Integer, ByRef hWnd As Integer, ByVal Msg As Integer, ByVal wParam As Integer, ByVal lParam As Integer) As Integer
    Private Declare Function GetLastError Lib "kernel32" () As Integer

    Sub New()
        Try
            '为进程申请4KB内存
            AllocBaseAddress = Marshal.AllocHGlobal(MEM_SIZE)
            ZeroMemory(AllocBaseAddress, MEM_SIZE)
            '初始化参数堆栈指针
            ClearCodeAndData()
            AddByte2Code(&H58) 'POP EAX
            AddByte2Code(&H59) 'POP ECX
            AddByte2Code(&H59) 'POP ECX
            AddByte2Code(&H59) 'POP ECX
            AddByte2Code(&H59) 'POP ECX
            AddByte2Code(&H50) 'PUSH EAX
        Catch ex As Exception
            Throw New Exception("RunRemoteASMCode类初始化错误", ex)
        End Try
    End Sub
    Public ReadOnly Property BaseAddress() As IntPtr
        Get
            Return AllocBaseAddress
        End Get
    End Property

    '将数据添加到申请的内存
    Protected Sub Add2Memory(ByVal Value() As Byte, ByVal AddressOffset As Integer)
        For addr As Integer = 0 To Value.Length - 1
            Marshal.WriteByte(AllocBaseAddress + AddressOffset + addr, Value(addr))
        Next
    End Sub

    '添加数据
    Public Function AddData(ByVal Value() As Byte) As Integer
        If ObjAddressOffset + Value.Length > MEM_SIZE Then
            Throw New Exception("数据超出所申请内存区域,无法继续")
        Else
            Dim ret As Integer = ObjAddressOffset
            Add2Memory(Value, ObjAddressOffset)                 '将数据写入“数据区”
            Dim odata As New mData                              '记录每个数据(地址和长度)
            odata.prt = ObjAddressOffset + AllocBaseAddress
            odata.len = Value.Length
            DataArraylist.Add(odata)

            ObjAddressOffset += Value.Length                    '堆栈数据指针向后移动
            ObjAddressOffset += 4 - (ObjAddressOffset Mod 4)    '四字节对齐

            Return ret
        End If
    End Function
    '添加整型
    Public Sub AddInt2Code(ByVal Value As Integer)
        Dim bytes() As Byte = BitConverter.GetBytes(CInt(Value))    '获取字节内容
        Add2Memory(bytes, PtrAddressOffset)                         '写入参数堆栈
        PtrAddressOffset += 4                                       '堆栈位置指针向后移动
    End Sub
    '添加字节型
    Public Sub AddByte2Code(ByVal Value As Byte)
        Dim bytes(0) As Byte
        bytes(0) = Value
        Add2Memory(bytes, PtrAddressOffset)
        PtrAddressOffset += 1
    End Sub
    '添加字节数组
    Public Sub AddBytes2Code(ByVal Value As Byte())
        Add2Memory(Value, PtrAddressOffset)
        PtrAddressOffset += Value.Length
    End Sub
    '添加CALL
    Public Sub AddCall2Code(ByVal FuncAddr As Integer)
        'E8指令要调用的地址是相对地址
        AddByte2Code(&HE8)
        Dim CallAddress As Integer = Math.Abs(AllocBaseAddress + PtrAddressOffset - FuncAddr) - 4
        AddInt2Code(CallAddress)
    End Sub

    '运行
    Function Run() As Integer
        Try
            AddByte2Code(&HC3)  'RET 10
            Return CallWindowProc(AllocBaseAddress, 0, 0, 0, 0)
        Catch ex As Exception
            Throw New Exception("GetLastError返回值为:" & GetLastError, ex)
        End Try
    End Function
    '清除代码和数据
    Public Sub ClearCodeAndData()
        PtrAddressOffset = 0        '初始化参数堆栈为所申请内存基地址
        ObjAddressOffset = 512      '从基地址向后偏移512字节供数据使用
        DataArraylist.Clear()
        ZeroMemory(AllocBaseAddress, MEM_SIZE)
    End Sub

    '读BYREF型返回值,例如GETWINDOWSTEXTA,第二个参数.要传回的BYREF参数索引,如GETWINDOWSTEXTA中,第二个参数是第一个BYREF传入值,要返回它则传入1</param>
    Public Function ReadBytesFromIndex(ByVal index As Integer) As Byte()
        Dim odata As mData = CType(DataArraylist(index - 1), mData)
        Dim ret(odata.len - 1) As Byte
        For i As Integer = 0 To odata.len - 1
            ret(i) = Marshal.ReadByte(odata.prt + i)
        Next
        Return ret
    End Function

    Protected Overrides Sub Finalize()
        Marshal.FreeHGlobal(AllocBaseAddress)    '释放申请的内存
        MyBase.Finalize()
    End Sub
    '用以记录数据段信息
    Protected Class mData
        Public prt As Integer   '地址
        Public len As Integer   '长度
    End Class
End Class


 测试:

 

记得先放一个BUTTON然后贴代码,不然丢了事件处理关联,代码不好使的

 

 


Public Class Form1
    Private Declare Function GetModuleHandle Lib "kernel32" Alias "GetModuleHandleA" (ByVal ModuleName As String) As IntPtr
    Private Declare Function GetProcAddress Lib "kernel32" (ByVal hModule As IntPtr, ByVal lpProcName As String) As IntPtr

    Dim asm As RunASMCode = New RunASMCode

    Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
        Debug.Print(Hex(asm.BaseAddress.ToInt32))
        'test : run messageboxw

        'get api address
        Dim msgwAddr As IntPtr = GetProcAddress(GetModuleHandle("user32"), "MessageBoxW")
        'Private Declare Function MessageBox Lib "user32" Alias "MessageBoxA" (ByVal hwnd As Long, ByVal lpText As String, ByVal lpCaption As String, ByVal wType As Long) As Long
        'push wType
        asm.AddByte2Code(&H68)
        asm.AddInt2Code(MsgBoxStyle.OkCancel)
        'push lpCaption
        asm.AddByte2Code(&H68)
        asm.AddInt2Code(asm.BaseAddress.ToInt32 + asm.AddData(System.Text.Encoding.Unicode.GetBytes("标题")))   'asm.AddData return DataAddressOffset
        'push lpText
        asm.AddByte2Code(&H68)
        asm.AddInt2Code(asm.BaseAddress.ToInt32 + asm.AddData(System.Text.Encoding.Unicode.GetBytes("内容")))
        'push hwnd
        asm.AddByte2Code(&H68)
        asm.AddInt2Code(&H0)
        'add call messagboxw
        asm.AddCall2Code(msgwAddr)
        'run asm
        Dim WretNum As Integer = asm.Run()

        Select Case WretNum
            Case MsgBoxResult.Ok
                Me.Text &= "按下了确认"
            Case MsgBoxResult.Cancel
                Me.Text &= "按下取消"
        End Select

    End Sub


    Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click

        'test GetWindowTextW
        asm.ClearCodeAndData()
        Dim txtw As IntPtr = GetProcAddress(GetModuleHandle("user32"), "GetWindowTextW")
        Dim s(1023) As Byte
        'Private Declare Function GetWindowText Lib "user32" Alias "GetWindowTextA" (ByVal hwnd As Long, ByVal lpString As String, ByVal cch As Long) As Long
        'push cch
        asm.AddByte2Code(&H68)
        asm.AddInt2Code(1024)
        'push lpString
        asm.AddByte2Code(&H68)
        asm.AddInt2Code(asm.BaseAddress.ToInt32 + asm.AddData(s))
        'push hwnd
        asm.AddByte2Code(&H68)
        asm.AddInt2Code(Me.Handle)
        'add call messagboxw
        asm.AddCall2Code(txtw)
        'run asm
        Dim WretNum As Integer = asm.Run()

        Dim mstr() As Byte = asm.ReadBytesFromIndex(1)
        MsgBox(System.Text.Encoding.Unicode.GetString(mstr), , WretNum)
    End Sub

End Class

 

 

 

 

 

尾巴~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~很短的

 

一般,我们处理

 

    Protected Overrides Sub WndProc(ByRef m As System.Windows.Forms.Message)
        MyBase.WndProc(m)
    End Sub
    Protected Overrides Sub DefWndProc(ByRef m As System.Windows.Forms.Message)
        MyBase.DefWndProc(m)
    End Sub

两个之一,来拦截一些本进程的消息之类,不过最近用的时候发现按键的没有,鼠标的倒是齐全!可后来一想,乖乖~~软软不至于吧,这么水?!不会搞了消息处理却是烂尾楼吧!于是左翻右翻~~吓!找到了,某网上某人疯狂复制粘贴搞理论,看蒙了(其实我蒙的很快的,只是膀胱扫射瞬间)……然后……别提了,其实很简单,整那么多没用的鸟臭氧层子啥用?

 

窗体里,直接在class form1下面来一行(当然冒号来也是很好,两种我都习惯),如是:

 Implements IMessageFilter

然后就发现,连函数都全自动生成了:


    Private Const WM_KEYDOWN As Integer = &H100
    Private Const WM_KeyUP As Integer = &H101
    Private Const WM_SYSKEYDOWN As Integer = &H104
    Private Const WM_SYSKEYUP As Integer = &H105
    Private Const WM_LBUTTONDOWN As Integer = &H201
    Private Const WM_LBUTTONUP As Integer = &H202
    Private Const WM_RBUTTONDOWN As Integer = &H204
    Private Const WM_RBUTTONUP As Integer = &H205
    Private Const WM_MOUSEWHEEL As Integer = &H20A

    Public Function PreFilterMessage(ByRef m As System.Windows.Forms.Message) As Boolean Implements System.Windows.Forms.IMessageFilter.PreFilterMessage
       Select Case m.Msg
            Case WM_KeyUP
                Dim keyCode As Keys = CType(m.WParam.ToInt32(), Keys) And Keys.KeyCode
                If keyCode = Keys.Add Then
  
                ElseIf keyCode = Keys.Subtract Then
  
                End If
            Case WM_LBUTTONDOWN
                
            Case WM_RBUTTONDOWN
                
        End Select
    End Function

 

其实也没生成这么多了,那是我的代码……将就一下吧,至少有好几个常数呢!

 

行了,消息过滤器实现了,可你要把它放哪啊?光有筛子哪行,老一辈筛沙子的程序员说得好:不筛沙子的筛子是没用的筛子……

于是,找个好地方让他筛吧!我是在form_load里面放上的,你随意,只要用的时候他在那候着!

    Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
        Application.AddMessageFilter(Me)
    End Sub

多简单的事,整那么大篇理论~~我就用,嘿嘿,改天去看理论,趴趴臭氧层子的好处就是,知点所以然,不过我还是喜欢reflector……尤其是破的

 

 

 

 

 

 

本来要写一个向端口写入数据模拟键盘输入的,不过遇到一些问题,想起来这个代码,再来修改一下,实际上内嵌汇编不用像开始介绍的那么麻烦:

内嵌汇编可以直接用MARSHAL类。PTR转委托,只需要声明一个委托的形式,而不用实际函数,实际函数直接写在PTR那里,调用时和实际的函数一样了。在XP、.NET FRAMEWORK 3.5下直接这样做就可以,但是在WIN7下测试的时候,发现总是LASTERR1400,用OD跟一下发现一运行到PTR那里直接错误处理了,跟了一下得到的结果是内存页属性中没有运行权限,所以用这个函数:

 

    <DllImport("kernel32.dll")> _
    Public Function VirtualProtectEx(ByVal hProcess As Integer, ByVal lpAddress As IntPtr, ByVal dwSize As Integer, ByVal flNewProtect As Integer, ByRef lpflOldProtect As Integer) As Integer
    End Function

flnewprotece用&H40就可以解决了。  第一个用process.getcursorprocess.handle,第二个是申请的内存地址,第三个。。。。1就行了,因为改内存块里面的任何字节的页属性都会导致整块的更改,最后一个多就是个“摆设”用个局部变量糊弄一下行了,改成读写运行(&H40之后一般没什么问题)。

 

 

posted @ 2011-01-14 11:40  zcsor~流浪dè风  Views(1797)  Comments(4Edit  收藏  举报