VERSION 1.0 CLASS
BEGIN
  MultiUse = -1  'True
  Persistable = 0  'NotPersistable
  DataBindingBehavior = 0  'vbNone
  DataSourceBehavior  = 0  'vbNone
  MTSTransactionMode  = 0  'NotAnMTSObject
END
Attribute VB_Name = "ucIntel32"
Attribute VB_GlobalNameSpace = False
Attribute VB_Creatable = True
Attribute VB_PredeclaredId = False
Attribute VB_Exposed = False
Option Explicit

'Unicorn Engine x86 32bit wrapper class for vb6

'Contributed by: FireEye FLARE team
'Author:         David Zimmer <david.zimmer@fireeye.com>, <dzzie@yahoo.com>
'License:        Apache

'we hide the extra labor of x64 conversion from the user. I could simplify
'this at the C shim layer but I might write an x64 class later
'
'since the vb long type only natively supports signed math, I have also handed off a couple
'calculations in this class to a C stub just to be safe.
'
'you can find a full unsigned and x64 safe library for vb6 here:
'    https://github.com/dzzie/libs/tree/master/vb6_utypes

Public hLib As Long
Public uc As Long
Public errMsg As String
Public Version As String
Public major As Long
Public minor As Long

Private r32 As Variant
Private r16 As Variant
Private r8 As Variant
Private rs_ As Variant
Private rs_Name As Variant
Private r32_Name As Variant
Private r16_Name As Variant
Private r8_Name As Variant
Private hooks As New Collection
Private m_DisasmOk As Boolean

Event CodeHook(ByVal address As Long, ByVal size As Long)
Event BlockHook(ByVal address As Long, ByVal size As Long)
Event MemAccess(ByVal t As uc_mem_type, ByVal address As Long, ByVal size As Long, ByVal value As Long)
Event InvalidMem(ByVal t As uc_mem_type, ByVal address As Long, ByVal size As Long, ByVal value As Long, ByRef continue As Boolean)
Event Interrupt(ByVal intno As Long)

'our vb enum is 0 based then mapped to the real C values so we can loop them to dump with name lookup
'these sub enums also keep the intellisense lists short and focused when reading/writing vals
'they are accessed through reg32, reg16, reg8, rs properties, or use raw full enum through reg property
'the names of each can be looked up through the reg32n etc properties
Public Enum reg_32
    eax_r = 0
    ecx_r = 1
    edx_r = 2
    ebx_r = 3
    esp_r = 4
    ebp_r = 5
    esi_r = 6
    edi_r = 7
End Enum

Public Enum reg_16
    ax_r = 0
    cx_r = 1
    dx_r = 2
    bx_r = 3
    sp_r = 4
    bp_r = 5
    si_r = 6
    di_r = 7
End Enum

Public Enum reg_8
    ah_r = 0
    ch_r = 1
    dh_r = 2
    bh_r = 3
    al_r = 4
    cl_r = 5
    dl_r = 6
    bl_r = 7
End Enum

Public Enum reg_Special
    CS_r = 0
    DS_r = 1
    ES_r = 2
    FS_r = 3
    GS_r = 4
    SS_r = 5
    IDTR_r = 6
    GDTR_r = 7
    LDTR_r = 8
End Enum

Property Get DisasmAvail() As Boolean
    DisasmAvail = m_DisasmOk
End Property

Property Get lastError() As Long
    lastError = ucs_errno(uc)
End Property

Property Get hadErr() As Boolean
    If Len(errMsg) > 0 Then hadErr = True
End Property

Property Get eip() As Long
    Dim e As uc_err, value As Long
    e = ucs_reg_read(uc, UC_X86_REG_EIP, value)
    eip = value
End Property

Property Let eip(v As Long)
    Dim e As uc_err
    e = ucs_reg_write(uc, UC_X86_REG_EIP, v)
End Property

Property Get eflags() As Long
    Dim e As uc_err, value As Long
    e = ucs_reg_read(uc, UC_X86_REG_EFLAGS, value)
    eflags = value
End Property

Property Let eflags(v As Long)
    Dim e As uc_err
    e = ucs_reg_write(uc, UC_X86_REG_EFLAGS, v)
End Property


'full access to all registers if you need it..
Property Get reg(r As uc_x86_reg) As Long
    Dim e As uc_err, value As Long
    e = ucs_reg_read(uc, r, value)
    reg = value
End Property

Property Let reg(r As uc_x86_reg, value As Long)
    Dim e As uc_err
    e = ucs_reg_write(uc, r, value)
End Property

'32 bit registers
Property Get reg32(r As reg_32) As Long
    Dim e As uc_err, value As Long
    If r < 0 Or r > UBound(r32) Then Exit Property
    e = ucs_reg_read(uc, r32(r), value)
    reg32 = value
End Property

Property Let reg32(r As reg_32, value As Long)
    Dim e As uc_err
    If r < 0 Or r > UBound(r32) Then Exit Property
    e = ucs_reg_write(uc, r32(r), value)
End Property

'16 bit registers
Property Get reg16(r As reg_16) As Long
    Dim e As uc_err, value As Long
    If r < 0 Or r > UBound(r16) Then Exit Property
    e = ucs_reg_read(uc, r16(r), value)
    reg16 = CInt(value)
End Property

Property Let reg16(r As reg_16, ByVal value As Long)
    Dim e As uc_err
    value = value And &HFFFF
    If r < 0 Or r > UBound(r16) Then Exit Property
    e = ucs_reg_write(uc, r16(r), value)
End Property

'8 bit registers
Property Get reg8(r As reg_8) As Long
    Dim e As uc_err, value As Long
    If r < 0 Or r > UBound(r8) Then Exit Property
    e = ucs_reg_read(uc, r8(r), value)
    reg8 = value
End Property

Property Let reg8(r As reg_8, ByVal value As Long)
    Dim e As uc_err
    value = value And &HFF
    If r < 0 Or r > UBound(r8) Then Exit Property
    e = ucs_reg_write(uc, r8(r), value)
End Property

'special registers
Property Get rs(r As reg_Special) As Long
    Dim e As uc_err, value As Long
    If r < 0 Or r > UBound(rs_) Then Exit Property
    e = ucs_reg_read(uc, rs_(r), value)
    rs = value
End Property

Property Let rs(r As reg_Special, ByVal value As Long)
    Dim e As uc_err
    If r < 0 Or r > UBound(rs_) Then Exit Property
    e = ucs_reg_write(uc, rs_(r), value)
End Property


'reg index to name translation for looping
Property Get reg32n(r As reg_32) As String
    If r < 0 Or r > UBound(r32_Name) Then Exit Property
    reg32n = r32_Name(r)
End Property

Property Get reg16n(r As reg_16) As String
    If r < 0 Or r > UBound(r16_Name) Then Exit Property
    reg16n = r16_Name(r)
End Property

Property Get reg8n(r As reg_8) As String
    If r < 0 Or r > UBound(r8_Name) Then Exit Property
    reg8n = r8_Name(r)
End Property

Property Get rsn(r As reg_Special) As String
    If r < 0 Or r > UBound(rs_Name) Then Exit Property
    rsn = rs_Name(r)
End Property

Function regDump(Optional includeState As Boolean = True) As String
    Dim i As Long
    Dim tmp As String
    
    For i = 0 To UBound(r32)
        tmp = tmp & reg32n(i) & "=" & Hex(reg32(i)) & "  "
        'if i mod 3 = 0 and i <> 0 then tmp = tmp & vbcrlf
    Next
    
    regDump = tmp
    
    If includeState Then
        regDump = regDump & "eip=" & Hex(Me.eip) & "  " & dumpFlags()
    End If
    
End Function

Function dumpFlags() As String
    
    Dim ret() As String
    Dim n As Variant
    Dim i As Long
    Dim flags As Long
    
    'http://www.c-jump.com/CIS77/ASM/Instructions/I77_0050_eflags.htm
    n = Array("C ", 0, "P ", 0, "A ", 0, "Z ", "S ", _
              "T ", "I ", "D ", "O ", "IOPL ", "IOPL ", "NT ", 0, _
              "RF ", "VM ", "AC ", "VIF ", "VIP ", "ID ", 0)

    flags = Me.eflags
    push ret, "EFL " & Hex(flags)

    For i = 0 To 21
        If flags And ULong(1, i, op_lsh) Then
            If n(i) <> 0 Then push ret, n(i)
        End If
    Next
    
    dumpFlags = Join(ret, "  ")
    
        
End Function

Private Sub Class_Initialize()
    
    Dim e As uc_err
    
    'mapping our simplified to real values..
    r32 = Array(UC_X86_REG_EAX, UC_X86_REG_ECX, UC_X86_REG_EDX, UC_X86_REG_EBX, UC_X86_REG_ESP, UC_X86_REG_EBP, UC_X86_REG_ESI, UC_X86_REG_EDI)
    r32_Name = Array("eax", "ecx", "edx", "ebx", "esp", "ebp", "esi", "edi")

    r16 = Array(UC_X86_REG_AX, UC_X86_REG_CX, UC_X86_REG_DX, UC_X86_REG_BX, UC_X86_REG_SP, UC_X86_REG_BP, UC_X86_REG_SI, UC_X86_REG_DI)
    r16_Name = Array("ax", "cx", "dx", "bx", "sp", "bp", "si", "di")

    r8 = Array(UC_X86_REG_AH, UC_X86_REG_CH, UC_X86_REG_DH, UC_X86_REG_BH, UC_X86_REG_AL, UC_X86_REG_CL, UC_X86_REG_DL, UC_X86_REG_Bl)
    r8_Name = Array("ah", "ch", "dh", "bh", "al", "cl", "dl", "bl")
  
    rs_ = Array(UC_X86_REG_CS, UC_X86_REG_DS, UC_X86_REG_ES, UC_X86_REG_FS, UC_X86_REG_GS, UC_X86_REG_SS, UC_X86_REG_IDTR, UC_X86_REG_GDTR, UC_X86_REG_LDTR)
    rs_Name = Array("cs", "ds", "es", "fs", "gs", "ss", "idtr", "gdtr", "ldtr")
    
    'just to ensure IDE finds the dll before we try to use it...
    Const dllName As String = "ucvbshim.dll"
    
    If Len(UNICORN_PATH) = 0 Then
        UNICORN_PATH = vbNullString
    ElseIf FolderExists(UNICORN_PATH) Then
        UNICORN_PATH = UNICORN_PATH & IIf(Right(UNICORN_PATH, 1) = "\", "", "\") & "unicorn.dll"
    End If
    
    If hLib = 0 Then
        hLib = GetModuleHandle(dllName)
        If hLib = 0 Then
            hLib = LoadLibrary(GetParentFolder(UNICORN_PATH) & "\" & dllName)
            If hLib = 0 Then
                hLib = LoadLibrary(dllName)
                If hLib = 0 Then
                    errMsg = "Could not load " & dllName
                    Exit Sub
                End If
            End If
        End If
    End If
    
    If DYNLOAD = 0 Then
        DYNLOAD = ucs_dynload(UNICORN_PATH)
        If DYNLOAD = 0 Then
            errMsg = "Dynamic Loading of unicorn.dll failed " & IIf(Len(UNICORN_PATH) > 0, "path: " & UNICORN_PATH, "")
            Exit Sub
        End If
    End If
    
    ucs_version major, minor
    Version = major & "." & minor
    
    If ucs_arch_supported(UC_ARCH_X86) <> 1 Then
        errMsg = "UC_ARCH_X86 not supported"
        Exit Sub
    End If
    
    e = ucs_open(UC_ARCH_X86, UC_MODE_32, uc)
    If e <> uc_err_ok Then
        errMsg = "Failed to create new x86 32bit engine instance " & err2str(e)
        Exit Sub
    End If
    
    If GetProcAddress(hLib, "disasm_addr") <> 0 Then m_DisasmOk = True
     
    instances.Add Me, "objptr:" & ObjPtr(Me)
    
End Sub

Private Sub Class_Terminate()
    If uc = 0 Then Exit Sub
    stopEmu
    ucs_close uc
    On Error Resume Next
    instances.Remove "objptr:" & ObjPtr(Me)
End Sub

Function mapMem(address As Long, size As Long, Optional protection As uc_prot = UC_PROT_ALL) As Boolean
    
    Dim addr As Currency
    Dim e As uc_err
    
    errMsg = Empty
    addr = lng2Cur(address)
   
    e = ucs_mem_map(uc, addr, size, protection)
    
    If e <> uc_err_ok Then
        errMsg = err2str(e)
        Exit Function
    End If
    
    mapMem = True
    
End Function

'address and size must be 4kb aligned, real buffer must be at least of size, and not go out of scope!
Function mapMemPtr(ByRef b() As Byte, address As Long, size As Long, Optional protection As uc_prot = UC_PROT_ALL) As Boolean
    
    Dim addr As Currency
    Dim e As uc_err
    
    errMsg = Empty
    addr = lng2Cur(address)
   
    If UBound(b) < size Then
        errMsg = "Buffer is < size"
        Exit Function
    End If
    
    If size Mod &H1000 <> 0 Then
        errMsg = "Size must be 4kb aligned"
        Exit Function
    End If
    
    If address Mod &H1000 <> 0 Then
        errMsg = "address must be 4kb aligned"
        Exit Function
    End If
   
    e = ucs_mem_map_ptr(uc, addr, size, protection, VarPtr(b(0)))
    
    If e <> uc_err_ok Then
        errMsg = err2str(e)
        Exit Function
    End If
    
    mapMemPtr = True
    
End Function

Function findAlloc(address As Long, Optional inRange As Boolean = False) As CMemRegion
    Dim m As CMemRegion
    Dim found As Boolean
    
    For Each m In getMemMap()
        If inRange Then
            If ULong(address, m.address, op_gteq) = 1 And ULong(address, m.address, op_lteq) = 1 Then found = True
        Else
            If m.address = address Then found = True
        End If
        If found Then
            Set findAlloc = m
            Exit Function
        End If
    Next
End Function

'we could accept a variant here instead of CMemRegion
'if typename(v) = "Long" then enum regions and find cmem, else expect CMemRegion..
'would be convient.. or a findAlloc(base as long) as CMemRegion
Function changePermissions(m As CMemRegion, newProt As uc_prot)
    Dim e As uc_err
    Dim addr64 As Currency

    errMsg = Empty

    If m Is Nothing Then Exit Function

    If newProt = m.perm Then
        changePermissions = True
        Exit Function
    End If

    addr64 = lng2Cur(m.address)

    e = ucs_mem_protect(uc, addr64, m.size, newProt)

    If e <> uc_err_ok Then
        errMsg = err2str(e)
        Exit Function
    End If

    m.perm = newProt
    changePermissions = True

End Function


Function unMapMem(base As Long) As Boolean

    Dim m As CMemRegion
    Dim e As uc_err
    Dim addr64 As Currency

    errMsg = Empty
    addr64 = lng2Cur(base)

    For Each m In getMemMap()
        If m.address = base Then
            e = ucs_mem_unmap(uc, addr64, m.size)
            unMapMem = (e = uc_err_ok)
            If Not unMapMem Then errMsg = err2str(e)
            Exit Function
        End If
    Next

End Function

'this function maps and writes (note 32bit only right now)
Function writeBlock(address As Long, buf() As Byte, Optional perm As uc_prot = UC_PROT_ALL) As Boolean
    
    Dim addr As Currency
    Dim e As uc_err
    
    addr = lng2Cur(address)
    
    errMsg = Empty
    e = mem_write_block(uc, addr, buf(0), UBound(buf) + 1, perm)
    If e <> uc_err_ok Then
        errMsg = err2str(e)
        Exit Function
    End If
    
    writeBlock = True
    
End Function

'this function requires the memory already be mapped in, use writeBlock for easier access...
Function writeMem(address As Long, buf() As Byte) As Boolean
    
    Dim addr As Currency
    Dim e As uc_err
    
    errMsg = Empty
    addr = lng2Cur(address)
    
    e = ucs_mem_write(uc, addr, buf(0), UBound(buf) + 1)
    If e <> uc_err_ok Then
        errMsg = err2str(e)
        Exit Function
    End If
    
    writeMem = True
    
End Function

Function writeByte(address As Long, b As Byte) As Boolean
    
    Dim addr As Currency
    Dim e As uc_err
    Dim buf(0) As Byte
    
    errMsg = Empty
    addr = lng2Cur(address)
    buf(0) = b
    
    e = ucs_mem_write(uc, addr, buf(0), 1)
    
    If e <> uc_err_ok Then
        errMsg = err2str(e)
        Exit Function
    End If
    
    writeByte = True
    
End Function

Function writeLong(address As Long, value As Long) As Boolean
    
    Dim addr As Currency
    Dim e As uc_err
    Dim buf(0 To 3) As Byte
    
    errMsg = Empty
    addr = lng2Cur(address)
    CopyMemory buf(0), ByVal VarPtr(value), 4
    
    e = ucs_mem_write(uc, addr, buf(0), 4)
    
    If e <> uc_err_ok Then
        errMsg = err2str(e)
        Exit Function
    End If
    
    writeLong = True
    
End Function

Function writeInt(address As Long, value As Integer) As Boolean
    
    Dim addr As Currency
    Dim e As uc_err
    Dim buf(0 To 1) As Byte
    
    errMsg = Empty
    addr = lng2Cur(address)
    CopyMemory buf(0), ByVal VarPtr(value), 2
    
    e = ucs_mem_write(uc, addr, buf(0), 2)
    
    If e <> uc_err_ok Then
        errMsg = err2str(e)
        Exit Function
    End If
    
    writeInt = True
    
End Function

Function readMem(address As Long, ByRef buf() As Byte, ByVal size As Long) As Boolean
    
    Dim addr As Currency
    Dim e As uc_err
    
    errMsg = Empty
    addr = lng2Cur(address)
    ReDim buf(size - 1) '0 based..
    
    e = ucs_mem_read(uc, addr, buf(0), UBound(buf) + 1)
    If e <> uc_err_ok Then
        errMsg = err2str(e)
        Exit Function
    End If
    
    readMem = True
    
End Function

Function readByte(address As Long, ByRef b As Byte) As Boolean
    
    Dim buf() As Byte
    
    readMem address, buf, 1
    If hadErr Then Exit Function

    b = buf(0)
    readByte = True

End Function

Function readLong(address As Long, ByRef retVal As Long) As Boolean
    
    Dim buf() As Byte
    
    readMem address, buf, 4
    If hadErr Then Exit Function

    CopyMemory ByVal VarPtr(retVal), buf(0), 4
    readLong = True
    
End Function

Function readInt(address As Long, ByRef retVal As Integer) As Boolean
    
    Dim buf() As Byte
    
    readMem address, buf, 2
    If hadErr Then Exit Function
    
    CopyMemory ByVal VarPtr(retVal), buf(0), 2
    readInt = True
    
End Function


Function saveContext() As Long
    
    Dim hContext As Long
    Dim e As uc_err
    
    errMsg = Empty
    e = ucs_context_alloc(uc, hContext)
    
    If e <> uc_err_ok Then
        errMsg = err2str(e)
        Exit Function
    End If
    
    e = ucs_context_save(uc, hContext)
    
    If e <> uc_err_ok Then
        errMsg = err2str(e)
        e = ucs_free(hContext)
        If e <> uc_err_ok Then errMsg = errMsg & " error freeing context: " & err2str(e)
        Exit Function
    End If
    
    saveContext = hContext
    
End Function

Function restoreContext(hContext As Long) As Boolean
    
    Dim e As uc_err
    
    errMsg = Empty
    e = ucs_context_restore(uc, hContext)
    
    If e <> uc_err_ok Then
        errMsg = err2str(e)
        Exit Function
    End If
    
    restoreContext = True
    
End Function

Function freeContext(hContext As Long) As Boolean
    Dim e As uc_err
    e = ucs_free(hContext)
    If e <> uc_err_ok Then
        errMsg = err2str(e)
    Else
        freeContext = True
    End If
End Function


Function disasm(va As Long, Optional ByRef instrLen As Long) As String

    Dim buf As String, i As Long, b() As Byte
    Dim dump As String
    On Error Resume Next
    
    If Not m_DisasmOk Then
        disasm = Right("00000000" & Hex(va), 8)
        Exit Function
    End If
    
    buf = String(300, Chr(0))
    
    instrLen = disasm_addr(uc, va, buf, Len(buf))
    If instrLen < 1 Then
        Select Case instrLen
            Case -1: buf = "Buffer to small"
            Case -2: buf = "Failed to read memory"
            Case -3: buf = "Failed to disassemble"
            Case Default: buf = "Unknown error " & instrLen
        End Select
        dump = "?? ?? ??"
        GoTo end_of_func
    End If
    
    i = InStr(buf, Chr(0))
    If i > 2 Then buf = VBA.Left(buf, i - 1) Else buf = Empty
    
    readMem va, b(), instrLen
    
    For i = 0 To UBound(b)
        dump = dump & hhex(b(i)) & " "
    Next
    
end_of_func:
    disasm = Right("00000000" & Hex(va), 8) & "  " & rpad(dump, 25) & buf
    
End Function

Function startEmu(beginAt As Long, endAt As Long, Optional timeout As Long = 0, Optional count As Long = 0) As Boolean
    
    Dim e As uc_err
    Dim a As Currency, b As Currency, t As Currency
    
    a = lng2Cur(beginAt)
    b = lng2Cur(endAt)
    t = lng2Cur(timeout)
    
    errMsg = Empty
    e = ucs_emu_start(uc, a, b, t, count)
    If e <> uc_err_ok Then
        errMsg = err2str(e)
        Exit Function
    End If

    startEmu = True
    
End Function

Function stopEmu() As Boolean
    Dim e As uc_err
    errMsg = Empty
    e = ucs_emu_stop(uc)
    If e <> uc_err_ok Then
        errMsg = err2str(e)
        Exit Function
    End If
    stopEmu = True
End Function


 Function addHook(catagory As hookCatagory, flags As uc_hook_type, Optional beginAt As Long = 1, Optional endAt As Long = 0) As Boolean

    Dim e As uc_err
    Dim hHook As Long 'handle to remove hook
    Dim a As Currency, b As Currency
    
    e = -1
    a = lng2Cur(beginAt)
    b = lng2Cur(endAt)
    errMsg = Empty
       
    If KeyExistsInCollection(hooks, "flags:" & flags) Then
        addHook = True
        Exit Function
    End If
    
    If catagory = hc_code Then e = ucs_hook_add(uc, hHook, flags, AddressOf code_hook, ObjPtr(Me), a, b, catagory)
    If catagory = hc_mem Then e = ucs_hook_add(uc, hHook, flags, AddressOf mem_hook, ObjPtr(Me), a, b, catagory)
    If catagory = hc_memInvalid Then e = ucs_hook_add(uc, hHook, flags, AddressOf invalid_mem_hook, ObjPtr(Me), a, b, catagory)
    If catagory = hc_block Then e = ucs_hook_add(uc, hHook, flags, AddressOf block_hook, ObjPtr(Me), a, b, catagory)
    If catagory = hc_int Then e = ucs_hook_add(uc, hHook, flags, AddressOf interrupt_hook, ObjPtr(Me), a, b, catagory)
    
    If e = -1 Then
        errMsg = "Unimplemented hook catagory"
        Exit Function
    End If
    
    If e <> uc_err_ok Then
        errMsg = err2str(e)
        Exit Function
    End If
    
    hooks.Add hHook, "flags:" & flags
    addHook = True
    
 End Function

'actually these appear to use different prototypes for each instruction? (only in/out examples seen...)
'what about all the others? not implemented yet in c or vb callback
'Function hookInstruction(i As uc_x86_insn, Optional beginAt As Long = 1, Optional endAt As Long = 0) As Boolean
'
'    Dim e As uc_err
'    Dim hHook As Long 'handle to remove hook
'    Dim a As Currency, b As Currency
'
'    If i = UC_X86_INS_INVALID Then Exit Function
'
'    e = -1
'    a = lng2Cur(beginAt)
'    b = lng2Cur(endAt)
'    errMsg = Empty
'
'    If KeyExistsInCollection(hooks, "instr:" & i) Then
'        hookInstruction = True
'        Exit Function
'    End If
'
'    e = ucs_hook_add(uc, hHook, UC_HOOK_INSN, AddressOf instruction_hook, ObjPtr(Me), a, b, hc_inst, i)
'
'    If e <> UC_ERR_OK Then
'        errMsg = err2str(e)
'        Exit Function
'    End If
'
'    hooks.Add hHook, "instr:" & i
'    hookInstruction = True
'
' End Function


Function removeHook(ByVal flags As uc_hook_type) As Boolean
    
    On Error Resume Next
    
    Dim hHook As Long, e As uc_err, wasInstr As Boolean
    
    errMsg = Empty
    hHook = hooks("flags:" & flags)
    
    If hHook = 0 Then
        hHook = hooks("instr:" & flags) 'maybe it was an instruction hook?
        If hHook = 0 Then
            errMsg = "Hook handle not found for supplied flags."
            Exit Function
        Else
           wasInstr = True
        End If
    End If
    
    e = ucs_hook_del(uc, hHook)
    
    If e <> uc_err_ok Then
        errMsg = err2str(e)
        Exit Function
    End If
    
    If wasInstr Then
        hooks.Remove "instr:" & flags
    Else
        hooks.Remove "flags:" & flags
    End If
    
    removeHook = True
    
End Function

Function getMemMap() As Collection 'of 32bit CMemRegion
    Dim c As New Collection
    Dim ret As New Collection
    Dim mem As CMemRegion
    Dim e As uc_err
    Dim s, tmp, v
    
    errMsg = Empty
    Set getMemMap = ret
    
    e = get_memMap(uc, c)
    
    If e <> uc_err_ok Then
        errMsg = err2str(e)
        Exit Function
    End If
    
    For Each s In c               '&h1000000,&h11fffff,&h7 these should always be 32bit safe values created in this class..
        If Len(s) > 0 Then
            tmp = Split(s, ",")
            If UBound(tmp) = 2 Then
                Set mem = New CMemRegion
                mem.address = CLng(tmp(0))
                mem.endsAt = CLng(tmp(1))
                mem.size = ULong(mem.endsAt, mem.address, op_sub) + 1 'vb native math is signed only..we play it safe..
                mem.perm = CLng(tmp(2))
                ret.Add mem
            End If
        End If
    Next
    
End Function


'these are internal functions used from the callback in the module to route the message to the event interface
'little confusing but in the end easier for the end user...also lays foundation for multiple live instances
'(although only one can run at a time since vb is single threaded)

Friend Function internal_invalid_mem_hook(ByVal t As uc_mem_type, ByVal address As Currency, ByVal size As Long, ByVal value As Currency) As Long
    Dim addr As Long, v As Long, continue As Boolean
    addr = cur2lng(address)
    v = cur2lng(value)
    RaiseEvent InvalidMem(t, addr, size, v, continue)
    internal_invalid_mem_hook = IIf(continue, 1, 0)
End Function

Friend Sub internal_mem_hook(ByVal t As uc_mem_type, ByVal address As Currency, ByVal size As Long, ByVal value As Currency)
    Dim addr As Long, v As Long
    addr = cur2lng(address)
    v = cur2lng(value)
    RaiseEvent MemAccess(t, addr, size, v)
End Sub

Friend Sub internal_code_hook(ByVal address As Currency, ByVal size As Long)
    Dim addr As Long
    addr = cur2lng(address)
    RaiseEvent CodeHook(addr, size)
End Sub

Friend Sub internal_block_hook(ByVal address As Currency, ByVal size As Long)
    Dim addr As Long
    addr = cur2lng(address)
    RaiseEvent BlockHook(addr, size)
End Sub

Friend Sub internal_interrupt_hook(ByVal intno As Long)
    RaiseEvent Interrupt(intno)
End Sub