プログラミング

vbscriptでマシン語を使う

2005年12月18日

 前の記事『VB.NETでマシン語を使う3』を書いていて思ったのだが、これは要するにvbscriptからマシン語を呼び出していることだということに気が付いた。ならばいっそのこと、vbscriptからマシン語を呼び出すためのCOMオブジェクトを用意しておけば、将来的にもしかしたら有用になるかも(?)しれない。で、作ってみた。

 次のようなコードで、vbscriptからマシン語が扱えるようにした。ここでは、ebx, edx に与えた値の積を、eaxとして返すようなマシン語を扱ってみた。
option explicit
dim ml,label1,label2,ebx,edx
set ml=CreateObject("dwtools.NET.MachineLanguage")
ebx=cInt(InputBox("ebx?"))
edx=cInt(InputBox("edx?"))
call asm
ml.Reset
call asm
ml.CallCode
msgbox cStr(ebx)+" X "+cStr(edx)+" = "+cStr(ml.eax)

sub asm()

  ml.AddCode("bb"):ml.AddInt32(ebx)           'mov       ebx,xxxxxxxx
  ml.AddCode("ba"):ml.AddInt32(edx)           'mov       edx,xxxxxxxx
  ml.AddCode("33c0")                          'xor       eax,eax
  ml.AddCode("b910000000")                    'mov       ecx,0x10
label1=ml.AddCode("f7c301000000")             'test      ebx,0x01
  ml.AddCode("0f84"):ml.AddInt32 label2,true  'je        label2
  ml.AddCode("03c2")                          'add       eax,edx
label2=ml.AddCode("c1eb01")                   'shr       ebx,0x01
  ml.AddCode("c1e201")                        'shl       edx,0x01
  ml.AddCode("e2"):ml.Addbyte label1,true     'loop      label1

end sub

 asm関数によるアセンブルを2回行っているのは、この手のアセンブル・コンパイルでは常套手段である。1回目でジャンプ命令のジャンプ先(label2)のアドレスが取得されるので、2回目で正しくje label2がアセンブルされる。

実行結果は、以下の通り。
ebx

edx

eax

 VB.NET部分のソースコードは以下の通りである。

option strict on
Imports System
Imports System.Runtime.InteropServices
Imports Microsoft.VisualBasic

'Class MachineLanguage contains:
'
'Public Function hKernel32() as integer
'Public Function AddressOfLoadLibrary() as integer
'Public Function AddressOfGetProcAddress() as integer
'Public eax as integer, edx as integer
'Public Sub Reset(ByVal cSize as integer)
'Public Function CallCode() as integer
'Public Function AddCode(ByVal T as string) as integer
'Public Function AddByte(ByVal b1 as integer, optional Relative as boolean=false) as integer
'Public Function AddInt16(ByVal s1 as integer, optional Relative as boolean=false) as integer
'Public Function AddInt32(ByVal i1 as integer, optional Relative as boolean=false) as integer
'Public Function AddAnsiString(ByVal T as string) as integer

Namespace dwtools.NET
Public Class MachineLanguage

  Declare Function EnumWindows Lib "user32" (x As Integer, y As Integer) As Integer
  Declare Function LoadLibrary Lib "kernel32" Alias "LoadLibraryA" (ByVal lpLibFileName As String) As Integer 
  Declare Function GetProcAddress  Lib "kernel32" Alias "GetProcAddress" (ByVal ModuleHandle As Integer, ByVal ProcName As String) As Integer
  Declare Function FreeLibrary Lib "kernel32" (ByVal hDll As Integer) As Integer

  Private _hKernel32 as integer=0
  Public Function hKernel32() as integer
    if _hKernel32<>0 then return _hKernel32
    _hKernel32=LoadLibrary("kernel32")
    return _hKernel32
  End Function
  Private _AddressOfLoadLibrary as integer=0
  Public Function AddressOfLoadLibrary() as integer
    if _AddressOfLoadLibrary<>0 then return _AddressOfLoadLibrary
    _AddressOfLoadLibrary=GetProcAddress(hKernel32,"LoadLibraryA")
    return _AddressOfLoadLibrary
  End Function
  Private _AddressOfGetProcAddress as integer=0
  Public Function AddressOfGetProcAddress() as integer
    if _AddressOfGetProcAddress<>0 then return _AddressOfGetProcAddress
    _AddressOfGetProcAddress=GetProcAddress(hKernel32,"GetProcAddress")
    return _AddressOfGetProcAddress
  End Function

  Public eax as integer, edx as integer
  Private Address as integer

  Private Buff(1) as integer
  Private GchBuff as GCHandle
  Private AsmCode() as byte
  Private AsmCodeSize as integer
  Private GchCode as GCHandle
  Private _cSize as integer=0
  Public Sub New()
    GchBuff=GCHandle.Alloc(Buff, GCHandleType.Pinned)
    call Reset(256)
  End Sub
  Public Sub Reset(optional ByVal cSize as integer=256)
    cSize=cSize+24 '24 bytes are used for initializing and terminating machine code.
    if _cSize<>cSize then 
      _cSize=cSize
      on error resume next
      GchCode.free()
      on error goto 0
      Redim Preserve AsmCode(cSize-1)
      GchCode=GCHandle.Alloc(AsmCode, GCHandleType.Pinned)
      Address=GchCode.AddrOfPinnedObject().ToInt32()
    end if
    
    AsmCodeSize=0
    
    'prepare code for storing esp
    AddCode("bb")
    AddInt32(GchBuff.AddrOfPinnedObject().ToInt32()) 'mov ebx,Buff
    AddCode ("89 23")                                'mov [ebx],esp
  End Sub

  Public Function CallCode() as integer
    'prepare code for returning
    AddCode("bb")
    AddInt32(GchBuff.AddrOfPinnedObject().ToInt32()) 'mov ebx,Buff
    AddCode("8b 23")                                 'mov esp,[ebx]
    AddCode("89 03")                                 'mov [ebx],eax
    AddCode("89 53 04")                              'mov [ebx+04],edx
    AddCode("33 c0")                                 'xor       eax,eax
    AddCode("c2 08 00")                              'ret 8
    
    'Call the machine code through EnumWindws
    EnumWindows(Address,0)
    eax=Buff(0)
    edx=Buff(1)
    
    return eax
  End Function

  Public Function AddCode(ByVal T as string) as integer
    Dim i as integer
    AddCode=AsmCodeSize+Address
    T=lcase(T)
    for i=1 to len(T)
      if 0<instr(1,"0123456789abcdef",mid(T,i,1)) then
        AddByte(cByte("&H"+mid(T,i,2)))
        i=i+1
      end if
    next i
  End Function
  Public Function AddByte(ByVal b1 as integer, optional Relative as boolean=false) as integer
    AddByte=AsmCodeSize+Address
    if Relative then 
      b1=b1-(AddByte+1)
      if b1<0 then b1=b1+256
    end if
    call AddByte(cByte(b1))
  End Function
  Private Function AddByte(b1 as byte) as integer
    AddByte=AsmCodeSize+Address
    AsmCode(AsmCodeSize)=b1
    AsmCodeSize=AsmCodeSize+1
  End Function
  Public Function AddInt16(ByVal s1 as integer, optional Relative as boolean=false) as integer
    AddInt16=AsmCodeSize+Address
    if Relative then 
      s1=s1-(AddInt16+2)
      if s1<0 then s1=s1+65536
    end if
    call AddInt16(cInt(s1))
  End Function
  Private Function AddInt16(s1 as short) as integer
    Dim T as string=right("000"+hex(s1),4)
    AddInt16=AsmCodeSize+Address
    AddByte(cByte("&H"+mid(T,3,2)))
    AddByte(cByte("&H"+mid(T,1,2)))
  End Function
  Public Function AddInt32(ByVal i1 as integer, optional Relative as boolean=false) as integer
    AddInt32=AsmCodeSize+Address
    if Relative then 
      i1=i1-(AddInt32+4)
    end if
    Dim T as string=right("00000000"+hex(i1),8)
    AddByte(cByte("&H"+mid(T,7,2)))
    AddByte(cByte("&H"+mid(T,5,2)))
    AddByte(cByte("&H"+mid(T,3,2)))
    AddByte(cByte("&H"+mid(T,1,2)))
  End Function
  Public Function AddAnsiString(ByVal T as string) as integer
    dim i as integer, b as integer
    AddAnsiString=AsmCodeSize+Address
    for i=1 to len(T)
      b=asc(mid(T,i,1))
      if 0<=b and b<=255 then
        AddByte(cByte(b))
      else 'Two byte code
        if b<0 then b=b+65536
        AddByte(cByte(b \ 256))
        AddByte(cByte(b mod 256))
      end if
    next i
    AddByte(0)
  End Function

End Class
End Namespace 'dwtools.NET

 Win32APIのうち、LoadLibraryとGetProcAddressは予め使えるようにしておいた。これで、マシン語からAPIを使いたいときも容易に行えるはずである。
 マシン語を追加する命令としては、複数バイトをHEX文字列として入力するAddCodeと、バイト型・16ビット整数型・32ビット整数型を入力可能なAddByte, AddInt16, AddInt32を用意した。後者の3つは第2パラメータにtrueを与えることで、相対jmp命令のジャンプ先を指定することが出来る。また、ANSI文字列を容易に扱えるようにするAddAnsiStringも作成しておいた。

(ここからダウンロードできます。)

コメント

コメントはありません

コメント送信