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.