也发个内嵌ASM,可用且可用!

        图省事,上网上找这个代码,我去,没烦死,五花八门,就是没有能运行的。可惜,笔记本报废的时候把硬盘嚼的烂烂的,然后之后最后还是狠下心来为了测试自己写了。

实现的思路挺多的,都能搞定这个事情。

1、callwindowproc额好像是这个名字,而且这个代码我硬盘上也有,但是明显慢。

     原理就是这个API接收一个地址并不检查就运行了。于是可以在内存里面构建一串ASM代码。

     用这个方法需要注意的是要把API函数本身的几个参数弹了,大概是push eax 然后4个pop ecx 再pop eax然后是我们的代码。

2、线程、远线程

     这个就是线程启动的时候可以传入地址,然后就运行了,不过也涉及到一些东西。不说了这个。

3、使用委托

     这个就是我们要介绍的东西了,实现方式也挺多的。像有的去声明一个实际函数,然后往里面写ASM,再去调用相应的委托。这里我们介绍的是GetDelegateForFunctionPointer方法以及相应的一些步骤等:

1、准备工作:

API声明、委托声明:

    Private Declare Function VirtualProtect Lib "kernel32.dll" (ByVal lpAddress As IntPtr, ByVal dwSize As Integer, ByVal flNewProtect As Integer, ByRef lpflOldProtect As Integer) As Integer

    Delegate Function subarg(arg1 As Integer, arg2 As Integer) As Integer

    Dim dlgt As subarg

感觉不用解释,就是API,subarg是函数的形式,dlgt是实例化的subarg。只需要注意dlgt这个要全局,不然GC说不定啥时候就给你把委托捣鼓没了。

2、准备工作

O(∩_∩)O~

为了方便用字符串形式表示ASM代码(用BYTE数组也行,可是不太舒服)写一个函数:

    Private Function Str2Bytes(str As String) As Byte()
        Dim i As Integer
        Dim assemblyCode As String
        assemblyCode = str.Replace(Space(1), String.Empty)
        Dim AssemblyValue(Len(assemblyCode) \ 2 - 1) As Byte
        For i = 1 To Len(assemblyCode) Step 2
            AssemblyValue(i \ 2) = Val("&H" & Mid(assemblyCode, i, 2))
        Next
        Return AssemblyValue
    End Function

这个更没什么好说的,就是为了用字符串表示,然后用空格啥的分开看起来清晰。

3、准备工作

我就不怕鸡蛋。写函数了,我加了个按钮。

    Private Sub Button1_Click(sender As System.Object, e As System.EventArgs) Handles Button1.Click
        Dim bs() = Str2Bytes(asmStr)
        Dim ptr As IntPtr = Marshal.AllocHGlobal(bs.Length)
        Dim lpflOldProtect As Integer
        VirtualProtect(ptr, 1, &H40, lpflOldProtect)    'rw+e
        Marshal.Copy(bs, 0, ptr, bs.Length)
        dlgt = Marshal.GetDelegateForFunctionPointer(ptr, GetType(subarg))
        MsgBox("当前可在所显示地址上下断点,观察函数调用情况。" & Hex(ptr.ToInt32), MsgBoxStyle.OkOnly, "函数汇编地址")
        Dim t As Integer = My.Computer.Clock.TickCount
        Dim i, r As Integer, n As Integer = 10000000
        For i = 0 To n - 1
            r = dlgt.Invoke(1, 2)
        Next
        Debug.Print(r & " " & n \ (My.Computer.Clock.TickCount - t))
        Marshal.FreeHGlobal(ptr)
    End Sub

简单解释一下,生成之后,一定要改一下内存属性,一般就是读写+运行了,反正也不恢复了,就不记录原始内存页属性,但是最好回收那块内存。

4、准备工作

- -!!!

其实前面的和绝大多数代码还是一样的,然后就是ASM了。这才是大头戏。一般来说,没有一点ASM造诣这个内嵌ASM不太好办,其实我就没有多点,知道的指令不超过5条,都是现用现查。如果自己写有问题,那么可以用VC写出来函数,然后用OD,IDA啥的看看,函数是什么样的作为参考。不过这样做完全照搬一般不行,因为那个函数里面有一些额外的东东。比较“正统”的做法应该是用ASM编程工具写代码然后链接编译,我一般会使用VC的内嵌汇编,因为不会用其他工具,连从哪打开都不知道- -!

这里我写了一个简单的函数,就是参数1 、 参数2作差:

    Dim asmStr As String = "55 8BEC" & _
                           "8B45 08 2B45 0C" & _
                           "5D C2 08 00"

仔细分析一下这个函数的由来:

A、用VC.NET建立一个MFC DLL,共享MFC那个,工程名subarg。(其实都无所谓了,差不多的事)

B、在subarg.cpp里面写上个函数,直接return a-b就行了。(复制上面的注释,稍微一改)

C、更改配置为release生成,然后OD或者IDA。分析代码,EAX做返回值,可以看到一些关键代码(IDA复制的,a,b其实是它的一种表示形式,如果是od就会看到mov sub那里分别写8,c):

a= dword ptr  8

b= dword ptr  0Ch

push    ebp

mov     ebp, esp

……

mov     eax, [ebp+a]
sub     eax, [ebp+b]

……

其实这就是了。上面还有一些寄存器的清理,用来做了一些别的事情,实际上我们就用一个EAX,所以其他没管。可能下面还有一些东西,但是EAX值就到这里了。当然,返回那里还清理了一些东西。但是为什么我的代码里面没有呢,其实用ASM编程工具写一个子函数看一下就知道了,这个子函数不用恢复。ret 8的意思就是说舍弃2个4,因为有2个参数,如果1个就是ret 4,如果3个就是……ret c

所以我们的函数全貌如下:

   push ebp           
   mov ebp,esp      


   mov eax,[ebp+8]   
   sub eax,[ebp+c]    


   pop ebp       
   RET 8            

直接从DIA里面抄就行,其实要是用OD,就直接打进去这些汇编就行了,CE、xuer什么都行。

最终的结果就是:

    Dim asmStr As String = "55 8BEC" & _
                           "8B45 08 2B45 0C" & _
                           "5D C2 08 00"


一行对应上面一段。全部代码都在上面了,下面附一份全的:

View Code
Imports System.Runtime.InteropServices

Public Class Form1
    Private Declare Function VirtualProtect Lib "kernel32.dll" (ByVal lpAddress As IntPtr, ByVal dwSize As Integer, ByVal flNewProtect As Integer, ByRef lpflOldProtect As Integer) As Integer

    '作为被调函数处理。因为参数是2个,所以舍弃8位。
    'push ebp           55
    'mov ebp,esp        8b ec
    '============实际函数============
    'mov eax,[ebp+8]    8b45 08
    'sub eax,[ebp+c]    2b45 0c
    '================================
    'pop ebp            5d
    'RET 8              c2 08 00

    Dim asmStr As String = "55 8BEC" & _
                           "8B45 08 2B45 0C" & _
                           "5D C2 08 00"
    Delegate Function subarg(arg1 As Integer, arg2 As Integer) As Integer

    Dim dlgt As subarg

    Private Sub Button1_Click(sender As System.Object, e As System.EventArgs) Handles Button1.Click
        Dim bs() = Str2Bytes(asmStr)
        Dim ptr As IntPtr = Marshal.AllocHGlobal(bs.Length)
        Dim lpflOldProtect As Integer
        VirtualProtect(ptr, 1, &H40, lpflOldProtect)    'rw+e
        Marshal.Copy(bs, 0, ptr, bs.Length)
        dlgt = Marshal.GetDelegateForFunctionPointer(ptr, GetType(subarg))
        MsgBox("当前可在所显示地址上下断点,观察函数调用情况。" & Hex(ptr.ToInt32), MsgBoxStyle.OkOnly, "函数汇编地址")
        Dim t As Integer = My.Computer.Clock.TickCount
        Dim i, r As Integer, n As Integer = 10000000
        For i = 0 To n - 1
            r = dlgt.Invoke(1, 2)
        Next
        Debug.Print(r & " " & n \ (My.Computer.Clock.TickCount - t))
        Marshal.FreeHGlobal(ptr)
    End Sub

    Private Function Str2Bytes(str As String) As Byte()
        Dim i As Integer
        Dim assemblyCode As String
        assemblyCode = str.Replace(Space(1), String.Empty)
        Dim AssemblyValue(Len(assemblyCode) \ 2 - 1) As Byte
        For i = 1 To Len(assemblyCode) Step 2
            AssemblyValue(i \ 2) = Val("&H" & Mid(assemblyCode, i, 2))
        Next
        Return AssemblyValue
    End Function


End Class

 

 

posted @ 2012-12-25 23:14  zcsor~流浪dè风  Views(425)  Comments(0Edit  收藏  举报