プログラミング

VB.NETでマシン語を使う

2005年12月14日

 現在のSFCは、Win32APIの殆どをVB.NET内で宣言する形になっている。これは、DynaWrapから脱却するための一つの方法であったのだが、逆にWin32API以外の関数をDLLから呼び出して実行することは出来ない(DynaWrapなら可能)。
 そこで、やはり最終的にはDynaCall様の物をVB.NET内に構築して実行する形にしたいと思っている。これには、DynaCallの様なインラインアセンブラが必要である。
 前に一度述べたようにこちらのページではインラインマシン語の使い方を紹介している。ただし、ver 2.0の.NET frameworkが必要で、これはまだβバージョンである。正式なver 2.0の登場が待てないので、別の形でインラインマシン語を利用する方法を考えてみた。

 前の記事では、Win32APIのEnumWindowsを利用すると、メモリ上に構築されたコードに制御が移ることを確認した。従って、マシン語のコードをメモリ上に構築し、同じ様に呼び出せばよいということである。かくしてできたテストプログラムが以下の通り

Imports System
Imports System.Runtime.InteropServices
Imports Microsoft.VisualBasic

Module Main
  Delegate Sub d_DcSub()
  Delegate Function d_EnumWindowsProc(hwnd As Integer, lParam As Integer) As Boolean
  Declare Function EnumWindows Lib "user32" (x As d_EnumWindowsProc, y As d_DcSub) As Integer
  Declare Function EnumWindows Lib "user32" (x As Integer, y As Integer) As Integer

  Sub Main(ByVal CmdArgs() As String)
  
    'Get the pointer of DcSub
    Dim dGate1 as d_DcSub=AddressOf DcSub
    EnumWindows(AddressOf EnumWindowsProc,dGate1)
    Dim p_DcSub as integer=fPointer
    Console.WriteLine("p_DcSub="+hex(p_DcSub))
    
    'Get address of DynaCall
    Dim dcAddress as integer=DynaCall
    Console.WriteLine("DynaCall address: "+hex(dcAddress))
    
    'Call the DynaCall through EnumWindws
    EnumWindows(dcAddress,p_DcSub)
  End Sub
  
  private fPointer as integer=0
  Function EnumWindowsProc(hwnd As Integer, lParam As Integer) As Boolean
    fPointer=lParam
    Return false
  End Function
  
  Private Sub DcSub() 
    Console.WriteLine("DcSub OK")
  End Sub
  
  private gch as GCHandle
  private AsmCode() as byte
  private AsmCodeSize as integer=0
  Private Function DynaCall() as integer
    AddCode(&h58)                   'pop       eax           //contains return address
    AddCode(&h58)                   'pop       eax           //contains hWnd
    AddCode(&h58)                   'pop       eax           //contains lParam (address of DcSub)
    AddCode(&h83,&hec,&h0c)         'sub       esp,0000000c  //return to original stack position
    AddCode(&hff,&hd0)              'call      eax           //call DcSub()
    AddCode(&h33,&hc0)              'xor       eax,eax       //eax=0
    AddCode(&hc2,&h08,&h00)         'ret       0008          //remove 8 bytes from stack and return eax
    gch = GCHandle.Alloc(AsmCode, GCHandleType.Pinned)
    return gch.AddrOfPinnedObject().ToInt32()
  End Function

  Private Sub AddCode(b1 as byte)
    Redim Preserve AsmCode(AsmCodeSize)
    AsmCode(AsmCodeSize)=b1
    AsmCodeSize=AsmCodeSize+1
  End Sub
  Private Sub AddCode(b1 as byte,b2 as byte)
    AddCode(b1)
    AddCode(b2)
  End Sub
  Private Sub AddCode(b1 as byte,b2 as byte,b3 as byte)
    AddCode(b1)
    AddCode(b2)
    AddCode(b3)
  End Sub
  Private Sub AddCode(b1 as byte,b2 as byte,b3 as byte,b4 as byte)
    AddCode(b1)
    AddCode(b2)
    AddCode(b3)
    AddCode(b4)
  End Sub

End Module

 作成したマシン語コードの中で別の関数(dcSub())を呼び出すことを試してみた。将来的には、複雑な操作はそういった関数の中で行い、マシン語でしかできない操作のみをこのコード内で行うように設定できるはずである。実行結果は、以下の通り

p_DcSub=15B26E
DynaCall address: 49A6470
DcSub OK

うまく行っているようである。

コメント

コメントはありません

コメント送信