zoukankan      html  css  js  c++  java
  • 【VB.NET】托管代码也可以处理HOOK API

    HOOK API是一个老生常谈的话题了,因为这种技术可以让我们在原有代码的基础上添加自己的其他代码进去,而实现我们的目的。对于C程序员来讲,这并不陌生,因为C语言可以编写标准的DLL文件,可以操作指针;对VB.NET程序员来讲,很多人都选择使用C语言去完成HOOK工作然后在VB.NET中进行调用,但实际上,并非如此。

    接下来我们详细介绍一下如何用VB.NETHOOK API

    一、HOOK本进程API并用托管函数处理

    如果你熟悉C语言的HOOK API的基本方式,那么可以直接看代码。

    1、查找要HOOKAPI的地址

    通常,我们使用API函数GetModleHandle和GetProcAddress。当然,也可以用VB.NET提供的Process.GetCurrentProcess.Modules来获取模块句柄之后解析PE结构来获取函数地址,但在一般应用当中有点小题大做。

    2、查找跳转到的函数的地址

    对于C程序员来讲,只需要利用函数指针即可(一个指向函数基地址的指针),但是VB.NET并不是不能取得函数指针,只是该函数应声明为一个委托的实例。如果你不了解委托可以先参考一下介绍委托的文章。将委托转化为可供非托管调用地址的函数就是让让你看了就灰常激动的marshal提供的Marshal.GetFunctionPointerForDelegate函数了。

    3、构造跳转

    只要有一点汇编基础只是即可,当然没有也可以。我们要实现这么一个流程:

    a修改原API,使之被调用时就跳转到我们的函数

    b在我们的函数中恢复原API并调用之

    重复以上步骤即可。一般我们使用jmp远跳转来实现。还有人总结了所谓“新入口计算公式”:lpAddress.ToInt32 - ProcAddress.ToInt32 5。我们只需要构造如下代码即可:

    Jmp oldFuncAddress

    Jmp newFuncAddress

    好了,万事俱备,上代码:

    【HOOK API类】很短小的

     

    Imports System.Runtime.InteropServices

    Public Class APIHOOK

        '巨注意:有的该死的版本里面marshal的读写方法滴偏移量参数无效,自己改改代码,把偏移直接加到基地址里面,偏移量用0 。有兴趣可以用Reflector察看一下。

        '有的同志习惯于用lstrcpyn这个api来取数组地址,方法就是把参数1,2都设置为数组本身,其返回值就是数组的内存地址。

        '虽然这是一种取变量非托管内存指针的有效方式。但这存在一些问题,建议还是使用marshal类直接在非托管内存操作。

     

        <DllImport("Kernel32.dll")> _

        Private Shared Function VirtualProtect(ByVal lpAddress As IntPtr, ByVal dwSize As Integer, ByVal flNewProtect As Integer, ByRef lpflOldProtect As Integer) As Boolean

        End Function

     

        <DllImport("Kernel32.dll")> _

        Private Shared Function GetProcAddress(ByVal hModule As IntPtr, ByVal lpProcName As String) As IntPtr

        End Function

     

        Const PAGE_EXECUTE_READWRITE As Integer = &H40          '内存保护属性

        Private ProcAddress As IntPtr                           'api函数地址

        Private lpflOldProtect As Integer = 0                   '原始内存保护属性

        Private OldEntry As IntPtr = Marshal.AllocHGlobal(5)    '原始入口点数据

        Private NewEntry As IntPtr = Marshal.AllocHGlobal(5)    '新入口点数据

        Private _Installed As Boolean

     

        Public ReadOnly Property Installed() As Boolean

            Get

                Return _Installed

            End Get

        End Property

     

        Public Sub New()

        End Sub

     

        Public Sub New(ByVal ModuleName As String, ByVal ProcName As String, ByVal lpAddress As IntPtr)

            Install(ModuleName, ProcName, lpAddress)

        End Sub

     

        Public Function Install(ByVal ModuleName As String, ByVal ProcName As String, ByVal lpAddress As IntPtr) As Boolean

            '模块句柄  

            Dim hModule As IntPtr

            For Each md As ProcessModule In Process.GetCurrentProcess.Modules

                If md.ModuleName.ToLower = ModuleName.ToLower Then

                    hModule = md.BaseAddress

                    Exit For

                End If

            Next

            If hModule = IntPtr.Zero Then Return False

            '函数入口 

            ProcAddress = GetProcAddress(hModule, ProcName)

            If ProcAddress = IntPtr.Zero Then Return False

            '修改内存属性

            If Not VirtualProtect(ProcAddress, 1, PAGE_EXECUTE_READWRITE, lpflOldProtect) Then Return False

            '------------------------------------------在非托管内存构造数据---------------------------------------------

            '读原始5字节

            For i As Integer = 0 To 4

                Marshal.WriteByte(OldEntry, i, Marshal.ReadByte(ProcAddress, i))

            Next

            '构造新5字节

            'jmp

            Marshal.WriteByte(NewEntry, 0, &HE9)

            '新入口地址——我们的处理函数的非托管地址,jmp是相对地址

            Marshal.WriteInt32(NewEntry, 1, lpAddress.ToInt32 - ProcAddress.ToInt32 - 5)

            '---------------------------------------------数据构造完毕-----------------------------------------------------

            _Installed = True

            Return True

        End Function

     

        Public Sub UnHook()

            If Not _Installed Then Return

            For i As Integer = 0 To 4

                Marshal.WriteByte(ProcAddress, i, Marshal.ReadByte(OldEntry, i))

            Next

        End Sub

     

        Public Sub Hook()

            If Not _Installed Then Return

            For i As Integer = 0 To 4

                Marshal.WriteByte(ProcAddress, i, Marshal.ReadByte(NewEntry, i))

                'Debug.Print(Marshal.ReadByte(NewEntry, i) & " " & Marshal.ReadByte(OldEntry, i))

            Next

        End Sub

     

        Public Function Uninstall() As Boolean

            If Not _Installed Then Return True

            UnHook()

            VirtualProtect(ProcAddress, 1, lpflOldProtect, lpflOldProtect)

            _Installed = True

            Return True

        End Function

     

     

    End Class

    【测试代码】也很短小

    Imports System.Runtime.InteropServices

    '函数原型

    'HttpOpenRequest  HINTERNET HttpOpenRequest (HINTERNET hInternet ,LPCTSTR lpszUrl ,LPCTSTR lpszHeaders ,DWORD dwHeadersLength ,DWORD dwFlags ,DWORD_PTR dwContext)

    Public Class Form1

     

        <DllImport("wininet.dll", CharSet:=CharSet.Ansi, setlasterror:=True)> _

        Private Shared Function HttpOpenRequestA(ByVal hHttpSession As Integer, ByVal sVerb As String, _

                          ByVal sObjectName As String, ByVal sVersion As String, _

                          ByVal sReferer As String, ByVal something As Integer, _

                          ByVal lFlags As Integer, ByVal lContext As Integer) As Integer

        End Function

        Private HttpOpenRequest_Hook As New APIHOOK()

        '定义一个引用变量以防止垃圾回收机制回收回调

        Private fix_COCD_HttpOpenRequest As HttpOpenRequestCallback

        Private Delegate Function HttpOpenRequestCallback(ByVal hHttpSession As Integer, ByVal sVerb As String, _

                          ByVal sObjectName As String, ByVal sVersion As String, _

                          ByVal sReferer As String, ByVal something As Integer, _

                          ByVal lFlags As Integer, ByVal lContext As Integer) As Integer

     

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

            WebBrowser1.Navigate("http://www.163.com")

            If HttpOpenRequest_Hook.Installed Then HttpOpenRequest_Hook.Uninstall()

            fix_COCD_HttpOpenRequest = New HttpOpenRequestCallback(AddressOf HttpOpenRequestProc)

            HttpOpenRequest_Hook.Install("wininet.dll", "HttpOpenRequestA", Marshal.GetFunctionPointerForDelegate(fix_COCD_HttpOpenRequest))

            HttpOpenRequest_Hook.Hook()

            Debug.Print("hook: " & HttpOpenRequest_Hook.Installed)

        End Sub

     

        Private Function HttpOpenRequestProc(ByVal hHttpSession As Integer, ByVal sVerb As String, _

                          ByVal sObjectName As String, ByVal sVersion As String, _

                          ByVal sReferer As String, ByVal something As Integer, _

                          ByVal lFlags As Integer, ByVal lContext As Integer) As Integer

            '注意:在钩CreateFile等函数时可能需要修改调试选项以便可以从非托管进入托管,并且不应直接使用debug.print等函数进行显示

            Debug.Print(sObjectName)

            '卸载钩子以便调用原函数

            HttpOpenRequest_Hook.UnHook()

            '调用原函数

            Dim ret As Integer = HttpOpenRequestA(hHttpSession, sVerb, sObjectName, sVersion, sReferer, something, lFlags, lContext)

            '加载钩子以便继续获取数据

            HttpOpenRequest_Hook.Hook()

            Return ret

        End Function

     

        Private Sub Form1_FormClosing(ByVal sender As Object, ByVal e As System.Windows.Forms.FormClosingEventArgs) Handles Me.FormClosing

            HttpOpenRequest_Hook.Uninstall()

        End Sub

     

     

    End Class

    可以看出,用托管代码处理HOOK的时候,流程和经典的HOOK处理没有什么区别,主要在于:

    1、声明一个托管函数,其原型要与HOOK的API原型一致(这里说的一致不一定是完全一样的类型,例如C里面字节指针,字符指针等都可以用byte数组代替,当然最好是明确封送约定了,不过我不习惯那样做,看起来乱七八糟的,都是用VB.NET提供的基本类型声明)

    2、实例化托管并将其转化为可供非托管代码调用的函数指针:Marshal.GetFunctionPointerForDelegate(fix_COCD_HttpOpenRequest))

    其实很简单是吧,呵呵,而且这里只列出了HOOK本进程的API。这是所写的一个工具的一部分,这个工具主要是用来注入托管代码到其他进程并HOOK其API函数,同时通过ebp取得调用堆栈,来进行一定的函数调用追踪的——当然可以用调试工具,前提是能调试…………哎,有些HOOK真的真的是……让VB.NET跑到内核里得写多少代码啊,那可要了亲命了,不如变通

    注入托管代码到非托管进程其实也不难,一般可以用一个VC的DLL写几句代码来加载CLR并进而加载并运行托管DLL,这方面的资料网上已经不少了,不罗列了。而获取ebp只是一个内嵌汇编过程,方法有很多种,一般来讲可以用远线程或本地线程、marshal类重构造委托代码或指针转化为委托等方式来做具体实现。在这篇文章中就不进行罗列了,因为还需要对ebp取得的call stack进行解码,基本的stack结构只能得到上层ebp,返回值,参数1,而还有很多工作需要做。

    你可以转发或修改代码,但请注明出处

  • 相关阅读:
    Linux 环境变量
    sharepoint webapp 部署注意点
    如何让html中的td文字只显示部分
    作业服务笔记
    HBase全分布安装配置
    CentOS Hadoop安装配置详细
    WCF访问超时:HTTP 请求已超过xx:yy分配的超时。为此操作分配的时间可能是较长超时的一部分。
    Apache Spark支持三种分布式部署方式 standalone、spark on mesos和 spark on YARN区别
    ext3文件系统挂载优化--HBase
    CentOS安装HBase
  • 原文地址:https://www.cnblogs.com/zcsor/p/2205098.html
Copyright © 2011-2022 走看看