Executing assembly language code in Visual Basic
Posted by: Zowayix
Date: 2013-06-10 13:08:58
This isn't a help topic or anything like that, but I figured this is so brilliant and at the same time so completely and utterly stupid that I should post it here to see if anyone else is as crazy as I am.
But it's not all my fault, I took the idea from here:
http://www.neowin.net/forum/topic/533650-vb6-hack/
I took it upon myself to rearrange the code to something I consider a bit less ugly (I'm not sure that other one actually works).
VERSION 1.0 CLASS
BEGIN
MultiUse = -1 'True
END
Attribute VB_Name = "AssemblyHax"
Attribute VB_GlobalNameSpace = False
Attribute VB_Creatable = False
Attribute VB_PredeclaredId = False
Attribute VB_Exposed = False
Option Explicit
#If VBA7 = 0 Then
Private Enum LongPtr
NULL_PTR = 0
End Enum
#Else
Private Const NULL_PTR as LongPtr = 0
#End If
Private Declare Function GlobalAlloc Lib "kernel32" (ByVal wFlags As Long, ByVal dwBytes As Long) As LongPtr
Private Declare Function GlobalLock Lib "kernel32" (ByVal hMem As LongPtr) As LongPtr
Private Declare Function GlobalFree Lib "kernel32" (ByVal hMem As LongPtr) As LongPtr
Private Declare Function GlobalUnlock Lib "kernel32" (ByVal hMem As LongPtr) As BOOL
Private Declare Sub GetMem4 Lib "msvbvm60" (ByVal addr as LongPtr, ByRef out as Long)
Private Declare Sub RtlMoveMemory Lib "kernel32" (dest As Any, src As Any, ByVal length As Long)
Private hMem As LongPtr
Private Function decodeBytes(s As String) As Byte()
Dim out() As Byte, i As Long
Dim size As Long
size = Len(s) \ 2
ReDim Preserve out(0 To size - 1)
For i = 1 To size
out(i - 1) = CByte("&H" & Mid$(s, i * 2 - 1, 2))
Next
decodeBytes = out
End Function
Private Function getLong(addr as LongPtr) as Long
GetMem4 addr, getLong
End Function
Function dontCallThisFunction(ByRef buf As Byte) As Long
End Function
Function executeASM(ByVal asm As String) As String
Dim b() As Byte
b = decodeBytes(asm)
Dim vTablePtr As LongPtr
vTablePtr = getLong(ObjPtr(Me))
Dim memPtr As Long
hMem = GlobalAlloc(0, Len(asm) \ 2)
memPtr = GlobalLock(hMem)
RtlMoveMemory ByVal memPtr, b(0), Len(asm) \ 2
GlobalUnlock hMem
RtlMoveMemory ByVal vTablePtr + &H1C, memPtr, 4
Dim buf(0 To 11) As Byte
dontCallThisFunction buf(0)
executeASM = StrConv(buf, vbUnicode)
End Function
Private Sub Class_Terminate()
If hMem <> NULL_PTR Then GlobalFree hMem
End Sub
Function cpuID() As String
cpuID = executeASM("578B7C240C33C00FA2891F895704894F085F33C0C3")
End Function
So you basically just create a new instance of AssemblyHax after importing this class, and call its cpuID method. The rest is magic.
Oh, alright. I'll attempt to explain said magic, and why this needs to be a class and not a standard module:
- COM objects (which all VB objects are) use virtual method tables internally
- executeASM first gets this object's actual memory address and dereferences the Long variable there to get the pointer to this v-table
- It then overrides the first function pointer of this v-table (hence it is important that dontCallThisFunction is the very first public function in order of declaration) with some arbitrary byte values, which is stored as a String because it's just easier that way
- Executes dontCallThisFunction which now actually has a method body (with those bytes), passing it a Byte array so it can store the returned values in some registers or however x86 assembly works
- Returns a string saying "GenuineIntel" or whatever
Feel free to throw up after reading this. I don't really blame you.
EDIT: Whoops, I forgot something I had in another module (namely the LongPtr hack) that this was taken from. Won't compile without it, so I put it in the private declarations. And the byteArray function wasn't even used. My stupid.