程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> 關於.NET >> 遠線程運行API

遠線程運行API

編輯:關於.NET

繼續前面一篇所寫的——遠線程調用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

在加一點點吧,可能類裡面有一個“機制”讓人費解。。就是提供了一個列 表,保存“按指針”傳遞的數據信息,還提供了一個RemoteBytesFromIndex函數 ,沒有示例理解起來要多看代碼。。

Dim mapi As RunRemoteAPI
Private Sub Button4_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button4.Click


mapi = New RunRemoteAPI(Shell("notepad.exe"))

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
mapi.CallRemoteAPIByName("user32.dll", "getwindowtextw", True, New mFuncParam(Me.Handle.ToInt32), New mFuncParam(s), New mFuncParam (s.Length))

Dim mstr = mapi.RemoteBytesFromIndex(1)
MessageBox.Show(System.Text.Encoding.Unicode.GetString(mstr))

mapi.RotateProcess.Kill()
mapi = Nothing

End Sub

基本思路就是打開一個記事本,由於SHELL返回值是“所調用的”EXE,說白 了就是參數裡面最先的EXE,例如說,調用CMD NOTEPAD.EXE,那返回的是CMD的 ID了;然後讓這個記事本運行GETWINDOWTEXTW函數,然後我們讀出在 NOTEPAD.EXE空間的返回值S數組的內容(由RemoteBytesFromIndex實現)並顯示 出來。

順便提一下,GETWINDOWTEXTA不成功,呵呵,是因為.NET都是用寬字符的原 因吧

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved