老掉牙的技术——远线程运行API

继续前面一篇所写的——远线程调用ASM

 

在上一篇中的类的基础上,继承并发扬了一个类:远线程运行API,里面采用的技术就是:

1、构造远线程调用代码及参数

2、通过线性搜索获取对方进程中的API入口地址

由于2是提取自一个以前的代码,并且调用API的函数中用了多个循环判定,导致……效率低下的很呢

代码中关键部分就是:

1、E8后面偏移地址的计算:E8后面是相对地址……

2、不同类型参数的处理:除了INTEGER以外,都“按指针”传递——调用时采用了字节数组

3、参数反向压栈:注意一下就可以了

4、实现了一个泛接口类,用以把结构转化为字节数组,但未测试,若不成功应自己把参数转化为字节数组后传入

5、有个修改内存页属性的函数,没有用到,其实是给后面程序用的,添加到这里了,要用也可以,只是在这个类里直接调用了API

 

这个完整的文件就是这样的了:(记得添加上篇那个类到工程才能用……继承并发扬么!)

 

 

Imports System.Runtime.InteropServices

Public Class RunRemoteAPI : Inherits RunRemoteASMCode
    ''' <summary>
    ''' 远程DLL函数导出函数信息
    ''' </summary>
    ''' <remarks></remarks>
    Protected avExports() As avExportOrImports
    ''' <summary>
    ''' 函数导出信息
    ''' </summary>
    ''' <remarks></remarks>
    Public Structure avExportOrImports
        Dim LibName As String       '库名(在导出中未使用)
        Dim ExportsIndex As Integer '函数导入/出序号
        Dim Name As String          '函数名
        Dim ExportsRVA As Integer   '函数导入/出表RVA
        Dim FunctionRVA As Integer  '函数入口RVA
    End Structure
    ''' <summary>
    ''' 基地址
    ''' </summary>
    ''' <value></value>
    ''' <returns>为对方进程申请的内存的基地址</returns>
    ''' <remarks></remarks>
    ReadOnly Property BaseAddress() As Integer
        Get
            Return MyBase.AllocBaseAddress
        End Get
    End Property
    ''' <summary>
    ''' 对方进程
    ''' </summary>
    ''' <value></value>
    ''' <returns>对方进程对象</returns>
    ''' <remarks></remarks>
    ReadOnly Property RotateProcess() As Process
        Get
            Return MyBase.RemoteProcess
        End Get
    End Property
    ''' <summary>
    ''' 用ProcessID初始化
    ''' </summary>
    ''' <param name="PID"></param>
    ''' <remarks></remarks>
    Sub New(ByVal PID As Integer)
        MyBase.New(PID)
    End Sub
    ''' <summary>
    ''' 根据名称调用对方进程的API
    ''' </summary>
    ''' <param name="DllName">API所在DLL名称</param>
    ''' <param name="FuncName">API函数名称</param>
    ''' <param name="FuncParams">参数列表</param>
    ''' <returns>API返回值,此返回值只有当Wait为True时才可信</returns>
    ''' <remarks></remarks>
    Function CallRemoteAPIByName(ByVal DllName As String, ByVal FuncName As String, ByVal Wait As Boolean, ByVal ParamArray FuncParams() As mFuncParam) As Integer
        If MyBase.RemoteProcess Is Nothing Then
            MsgBox("未找到指定进程")
            Return -1
        End If
        '初始化数据
        ClearCodeAndData()
        Dim DllHandle As Integer = -1
        Dim FuncAddress As Integer = -1
        '枚举对方进程模块列表,找到对方进程中相应DLL的基地址(Handle)
        For Each m As ProcessModule In Process.GetProcessById(MyBase.RemoteProcess.Id).Modules
            If InStr(m.FileName.ToUpper, DllName.ToUpper) > 0 Then
                DllHandle = m.BaseAddress
                Exit For
            End If
        Next
        If DllHandle = -1 Then
            MsgBox("未发现对方进程中的 " & DllName & " 模块", , "进程 : " & MyBase.RemoteProcess.Id)
            Return -1
        End If
        '枚举对方进程中相应DLL中全部函数信息(暴力搜索)
        avExports = GetExports(MyBase.RemoteProcess.Handle, DllHandle)
        '遍历信息表,获取我们需要的函数入口地址
        For Each e As avExportOrImports In avExports
            If Not e.Name Is Nothing AndAlso e.Name.ToUpper = FuncName.ToUpper Then
                FuncAddress = e.FunctionRVA
                Exit For
            End If
        Next
        If FuncAddress = -1 Then
            MsgBox("未发现 " & DllName & " 模块中的函数 " & FuncName, , "进程 : " & MyBase.RemoteProcess.Id)
            Return -1
        End If
        '以下构造调用API的ASM CODE
        '首先将参数依次压栈
        Dim Ubound As Integer = FuncParams.Length - 1
        For i As Integer = Ubound To 0 Step -1                              '参数反向
            If FuncParams(i).Ptr Then                                       '如果需要按指针封送,作为数据处理并添加指针段。否则,直接添加
                Dim addr As Integer = MyBase.AddData(FuncParams(i).Obj)
                MyBase.AddByte2Code(&H68)                                   '按4字节对齐 prush
                MyBase.AddInt2Code(addr + MyBase.AllocBaseAddress)          '将数据部分的地址写入到为对方申请的内存的代码部分
            Else
                MyBase.AddByte2Code(&H68)                                   '按4字节对齐
                MyBase.AddBytes2Code(FuncParams(i).Obj)
            End If
        Next
        '向代码添加调用
        AddCallToCode(FuncAddress)
        '向代码添加RET(RET 10)
        MyBase.AddByte2Code(&HC3)
        'int3
        MyBase.AddByte2Code(&HCC)
        Return MyBase.Run(Wait)
    End Function
    ''' <summary>
    ''' 读BYREF型返回值,例如GETWINDOWSTEXTA,第二个参数
    ''' </summary>
    ''' <param name="index">要传回的BYREF参数索引,如GETWINDOWSTEXTA中,第二个参数是第一个BYREF传入值,要返回它则传入1</param>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Public Function RemoteBytesFromIndex(ByVal index As Integer) As Byte()
        Dim odata As mData = CType(MyBase.DataArraylist(index - 1), mData)
        Dim ret(odata.len - 1) As Byte
        ReadProcessMemory(MyBase.RemoteProcess.Handle, odata.prt, ret, odata.len, 0)
        Return ret
    End Function
    ''' <summary>
    ''' 将CALL语句添加到ASM CODE
    ''' </summary>
    ''' <param name="FuncAddr">API函数入口地址</param>
    ''' <remarks>注意地址偏移计算</remarks>
    Protected Sub AddCallToCode(ByVal FuncAddr As Integer)
        'E8指令要调用的地址是相对地址
        MyBase.AddByte2Code(&HE8)
        Dim CallAddress As Integer = Math.Abs(MyBase.AllocBaseAddress + MyBase.PtrAddressOffset - FuncAddr) - 4
        MyBase.AddInt2Code(CallAddress)
    End Sub

    ''' <summary>
    ''' 获取指定DLL函数的导出函数信息
    ''' </summary>
    ''' <param name="pHandle">DLL所在进程句柄</param>
    ''' <param name="ModuleBaseAddress">DLL句柄(基地址)</param>
    ''' <remarks></remarks>
    Public Function GetExports(ByVal pHandle As IntPtr, ByVal ModuleBaseAddress As Integer) As avExportOrImports()
        Dim ret() As avExportOrImports = Nothing
        Try
            Dim lpEXPORT_TABLE As Integer = ModuleBaseAddress + MemValue(pHandle, ModuleBaseAddress + MemValue(pHandle, ModuleBaseAddress + &H3C) + &H78)
            Dim lNumberOfNames As Integer = MemValue(pHandle, lpEXPORT_TABLE + &H18)
            Dim lNumberOfFunctions As Integer = MemValue(pHandle, lpEXPORT_TABLE + &H14)
            Dim lBase As Integer = MemValue(pHandle, lpEXPORT_TABLE + &H10)
            Dim lpNamesTable As Integer = ModuleBaseAddress + MemValue(pHandle, lpEXPORT_TABLE + &H20)
            Dim lpFunctionsTable As Integer = ModuleBaseAddress + MemValue(pHandle, lpEXPORT_TABLE + &H1C)
            Dim lpOrdinalsTable As Integer = ModuleBaseAddress + MemValue(pHandle, lpEXPORT_TABLE + &H24)
            Dim lpFunction As Integer
            Dim lNameOrdinal As Integer
            ReDim ret(lNumberOfFunctions - 1)
            Dim i As Integer
            For i = 0 To lNumberOfFunctions - 1     '识别入口
                ret(i).ExportsIndex = i + lBase
                ret(i).ExportsRVA = lpFunctionsTable + 4 * i
                ret(i).FunctionRVA = MemValue(pHandle, lpFunctionsTable + 4 * i) + ModuleBaseAddress
            Next
            Do While lNumberOfNames > 0     '从入口识别函数名
                lNumberOfNames = lNumberOfNames - 1
                lpFunction = ModuleBaseAddress + MemValue(pHandle, lpNamesTable + lNumberOfNames * 4)
                lNameOrdinal = MemValue(pHandle, (lpOrdinalsTable + lNumberOfNames * 2), True)
                If lNameOrdinal >= lNumberOfFunctions Then Exit Do
                ret(lNameOrdinal).Name = RemoteStrFromPtr(pHandle, lpFunction)
            Loop
            Return ret
        Catch ex As Exception
            'Debug.Print(Err.Description)
            Return ret
        End Try
    End Function

    ''' <summary>
    ''' 读取指定内存4,2字节
    ''' </summary>
    ''' <param name="lAddress">地址</param>
    ''' <param name="TooByte">二字节还是四字节</param>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Protected Function MemValue(ByVal pHandle As IntPtr, ByVal lAddress As Integer, Optional ByVal TooByte As Boolean = False) As Object
        Dim tmpArr() As Byte
        If TooByte Then ReDim tmpArr(1) Else ReDim tmpArr(3)
        Try
            Dim lOldProtect As Integer
            VirtualProtectEx(pHandle, lAddress, 1, &H40, lOldProtect)
            ReadProcessMemory(pHandle, lAddress, tmpArr, tmpArr.Length, 0)
            VirtualProtectEx(pHandle, lAddress, 1, lOldProtect, lOldProtect)
        Catch ex As Exception
            If Err.Number = 5 Then Return 0
        End Try
        If TooByte Then
            Return BitConverter.ToInt16(tmpArr, 0)
        Else
            Return BitConverter.ToInt32(tmpArr, 0)
        End If
    End Function

    ''' <summary>
    ''' 根据内存地址指针读字符串
    ''' </summary>
    ''' <param name="lpString">地址(指针)</param>
    ''' <returns></returns>
    ''' <remarks>最多读会1024个字符</remarks>
    Protected Function RemoteStrFromPtr(ByVal pHandle As IntPtr, ByVal lpString As Integer) As String
        Dim b(1023) As Byte
        Dim lPosOfZero As Integer
        Dim lOldProtect As Integer
        Try
            VirtualProtectEx(pHandle, lpString, 1, &H40, lOldProtect)
            ReadProcessMemory(pHandle, lpString, b, 1024, 0)
            VirtualProtectEx(pHandle, lpString, 1, lOldProtect, lOldProtect)
            Dim i As Integer
            For i = 0 To b.Length - 1
                If b(i) = 0 Then
                    lPosOfZero = i
                    Exit For
                End If
            Next
            Return System.Text.Encoding.ASCII.GetString(b, 0, lPosOfZero)
        Catch ex As Exception
            'Debug.Print(Err.Number & " " & Err.Description)
            Return String.Empty
        End Try
    End Function
    Enum Protect    '内存保护属性枚举
        PAGE_NOACCESS = &H1
        PAGE_READONLY = &H2
        PAGE_READWRITE = &H4
        PAGE_WRITECOPY = &H8
        PAGE_EXECUTE = &H10
        PAGE_EXECUTE_READ = &H20
        PAGE_EXECUTE_READWRITE = &H40
        PAGE_EXECUTE_READWRITECOPY = &H50
        PAGE_EXECUTE_WRITECOPY = &H80
        PAGE_GUARD = &H100
        PAGE_NOCACHE = &H200
        PAGE_WRITECOMBINE = &H400
    End Enum

    Shared Function SetVirtualProtect(ByVal hProcess As IntPtr, ByVal lpBaseAddress As Integer, Optional ByVal nSize As Integer = 8, Optional ByVal nProtect As Protect = Protect.PAGE_EXECUTE_READWRITECOPY) As Integer
        Dim lProtect As Integer
        If VirtualProtectEx(hProcess, lpBaseAddress, nSize, nProtect, lProtect) <> 0 Then Return lProtect Else Return -1
    End Function

End Class

''' <summary>
''' 函数参数
''' </summary>
''' <remarks></remarks>
Public Class mFuncParam
    Public Obj As Byte()
    Public Ptr As Boolean
    Sub New(ByVal obj As Integer)
        Me.Obj = BitConverter.GetBytes(obj)
        Me.Ptr = False
    End Sub
    Sub New(ByVal obj As Byte())
        Me.Obj = obj.Clone
        Me.Ptr = True
    End Sub
End Class

''' <summary>
''' 用于将结构体转化为BYTE类型,只提供了一个共享成员方法
''' </summary>
''' <typeparam name="T">必须传入自定义类型,系统类型将引发错误</typeparam>
''' <remarks>系统类型应自行转化为BYTE数组或INTEGER类型</remarks>
Public Class Param(Of T)
    Shared Function GetBytes(ByVal Value As T) As Byte()
        Try
            Dim size As Integer = Marshal.SizeOf(Value)
            Dim ret(size) As Byte
            Dim ptr As IntPtr = Marshal.AllocHGlobal(size)
            Marshal.StructureToPtr(Value, ptr, True)
            Marshal.Copy(ptr, ret, 0, size)
            Marshal.FreeHGlobal(ptr)
            Return ret
        Catch ex As Exception
            MsgBox(ex.ToString)
            Return Nothing
        End Try
    End Function
End Class

 

 

示例就是这样了:

    Dim api As RunRemoteAPI

 

 

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

        ListBox1.Items.Clear()
        ListBox2.Items.Clear()

        Dim mPathName As String = "G:\HOOKRECV\Debug\HOOKRECV.dll" ' My.Application.Info.DirectoryPath & "\test.dll"

        api = New RunRemoteAPI(CInt(TextBox1.Text))
        ‘’RunRemoteASMCode.SetCPUID() ‘’这个还是注释掉好,因为写HOOK API 的时候出了点小插曲,以为是CPU多核心引起的,实际不然,而且把这个作为上一篇基类当中的一个函数也不恰当了。。。呵呵

        TextBox3.Text = Hex(api.CallRemoteAPIByName("kernel32", "LoadLibraryW", True, New mFuncParam(System.Text.Encoding.Unicode.GetBytes(mPathName))))

        TextBox2.Text = Hex(api.BaseAddress)

        '枚举对方进程模块列表
        For Each m As ProcessModule In Process.GetProcessById(api.RotateProcess.Id).Modules
            ListBox1.Items.Add(m.FileName)
        Next


    End Sub

 

把上面注释掉的那个函数写在这:

    Public Shared Sub SetCPUID(Optional ByVal PID As Integer = -1, Optional ByVal CPUID As Integer = 1)
        Dim CPUIDPTR As IntPtr = New IntPtr(CPUID)
        Dim Ths As ProcessThreadCollection
        If PID = -1 Then Ths = Process.GetCurrentProcess.Threads Else Ths = Process.GetProcessById(PID).Threads
        For Each th As ProcessThread In Ths
            th.ProcessorAffinity = CPUIDPTR
        Next
    End Sub

实际上就是指定一个进程的所有线程都运行在1号CPU核心上,当然也可以指定,2号为2,3号为4(因为同时用1,2时是3嘛!)。

 

 

迄今用VC写的那个HOOK API的DLL还是不行呢,准备用点外科手段消灭掉对WriteProcessMemory的调用,也就是不来回切换代码,虽然WriteProcessMemory自行刷新缓存,多核心没问题,可多线程问题就来了,还是不要倒来倒去的,直接改成“一条龙”服务……

另外写DLL实际上主要是为了HOOK某些游戏的封包,如果单是API的话……呵呵,不用这么麻烦,用VB.NET完全可以了。。。前面那个线性搜索就是几年前写的HOOK API里面的函数,当时用的HOOK方法是使用调试函数,在API开头修改一个字节为INT3……然后CONTEXT里面的东东……就都出来了…………

 

 

恩……编辑一下,才想起来,用了不少进程操作的函数,有时候没有这个不行:

 

 

Public Class SeDebugPrivilege
#Region "常数及结构声明"
    Private Const SE_PRIVILEGE_ENABLED As Int32 = 2
    Private Const EWX_SHUTDOWN As Int32 = 1
    Private Const EWX_REBOOT As Int32 = 2
    Private Const EWX_LOGOFF As Int32 = 0
    Private Structure LUID_AND_ATTRIBUTES
        Public pLuid As LUID
        Public Attributes As Integer
    End Structure

    Private Structure LUID
        Dim LowPart As Int32
        Dim HighPart As Int32
    End Structure

    Private Structure TOKEN_PRIVILEGES
        Public PrivilegeCount As Integer
        Public Privileges As LUID
        Public Attributes As Int32
    End Structure
#End Region

#Region "API声明"
    Private Declare Function LookupPrivilegeValue Lib "advapi32.dll" Alias "LookupPrivilegeValueA" (ByVal lpSystemName As String, ByVal lpName As String, ByRef lpLuid As LUID) As Int32
    Private Declare Function AdjustTokenPrivileges Lib "advapi32.dll" (ByVal TokenHandle As IntPtr, ByVal DisableAllPrivileges As Int32, ByRef NewState As TOKEN_PRIVILEGES, ByVal BufferLength As Int32, ByRef PreviousState As TOKEN_PRIVILEGES, ByRef ReturnLength As Int32) As Int32
    Private Declare Function OpenProcessToken Lib "advapi32.dll" (ByVal ProcessHandle As IntPtr, ByVal DesiredAccess As Integer, ByRef TokenHandle As IntPtr) As Boolean
    'Private Declare Function OpenThreadToken Lib "advapi32.dll" (ByVal ThreadHandle As IntPtr, ByVal DesiredAccess As Integer, ByVal OpenAsSelf As Integer, ByVal TokenHandle As IntPtr) As Integer
#End Region

#Region "获取全部权限"
    Public Function ToKenPrivileges() As Boolean
        Dim hdlTokenHandle As Integer
        Dim tmpLuid As LUID
        Dim tkp As TOKEN_PRIVILEGES
        Dim tkpNewButIgnored As TOKEN_PRIVILEGES
        Dim lBufferNeeded As Integer
        Dim currentProcess As Process = Process.GetCurrentProcess()
        If OpenProcessToken(currentProcess.Handle, &HF00FF, hdlTokenHandle) Then
            LookupPrivilegeValue("", "SeDebugPrivilege", tmpLuid)
            tkp.PrivilegeCount = 1
            tkp.Privileges = tmpLuid
            tkp.Attributes = SE_PRIVILEGE_ENABLED
            Return AdjustTokenPrivileges(hdlTokenHandle, False, tkp, Len(tkpNewButIgnored), tkpNewButIgnored, lBufferNeeded)
        End If
    End Function
#End Region
End Class

 

 

工程里添加如下调用即可…………

    Dim SeDebug As New SeDebugPrivilege
    Private Sub Form1_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
        If SeDebug.ToKenPrivileges = False Then MsgBox("提升权限失败")
    End Sub

 

posted @ 2009-04-13 09:15  zcsor~流浪dè风  Views(2721)  Comments(4Edit  收藏  举报