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