vbscriptでマシン語を使う
2005年12月18日
前の記事『VB.NETでマシン語を使う3』を書いていて思ったのだが、これは要するにvbscriptからマシン語を呼び出していることだということに気が付いた。ならばいっそのこと、vbscriptからマシン語を呼び出すためのCOMオブジェクトを用意しておけば、将来的にもしかしたら有用になるかも(?)しれない。で、作ってみた。
次のようなコードで、vbscriptからマシン語が扱えるようにした。ここでは、ebx, edx に与えた値の積を、eaxとして返すようなマシン語を扱ってみた。
asm関数によるアセンブルを2回行っているのは、この手のアセンブル・コンパイルでは常套手段である。1回目でジャンプ命令のジャンプ先(label2)のアドレスが取得されるので、2回目で正しくje label2がアセンブルされる。
実行結果は、以下の通り。



VB.NET部分のソースコードは以下の通りである。
Win32APIのうち、LoadLibraryとGetProcAddressは予め使えるようにしておいた。これで、マシン語からAPIを使いたいときも容易に行えるはずである。
マシン語を追加する命令としては、複数バイトをHEX文字列として入力するAddCodeと、バイト型・16ビット整数型・32ビット整数型を入力可能なAddByte, AddInt16, AddInt32を用意した。後者の3つは第2パラメータにtrueを与えることで、相対jmp命令のジャンプ先を指定することが出来る。また、ANSI文字列を容易に扱えるようにするAddAnsiStringも作成しておいた。
(ここからダウンロードできます。)
次のようなコードで、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 subasm関数によるアセンブルを2回行っているのは、この手のアセンブル・コンパイルでは常套手段である。1回目でジャンプ命令のジャンプ先(label2)のアドレスが取得されるので、2回目で正しくje label2がアセンブルされる。
実行結果は、以下の通り。



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