Código ensamblador en Visual Basic 6
Ensamblador en VB6
Tenía guardado en mi disco duro dos ejemplos de cómo ejecutar código ensamblador en Visual Basic 6. Aunque VB6 va poco a poco perdiendo adeptos, antes de borrar estos dos ejemplos prefiero compartirlos y tal vez a alguno todavía le sirva. En este tutorial voy a ejecutar un simple código y mostraré cómo extraerlo usando por ejemplo OllyDBG (v1.10 - v2). En el siguiente artículo que escriba sobre este tema, mostraré otra forma de realizarlo y se podrá observar la velocidad del mismo código en VB6 y en asm.
Para ejecutar código asm en VB6 se suelen utilizar dos alternativas:
- Mediante el uso de una herramienta externa (complemento)
- Programando "a mano" creando código asm inline (en línea)
Para este primer ejemplo voy a programarlo a mano. El código que tengo guardado es una variante que he hecho, basándome en un código muy sencillo que encontré hace ya unos años en este enlace:
http://foro.elhacker.net/programacion_vb/srcsnippet_tecnica_antidebugging_asmvb_callwindowproc-t244267.0.html. Un código que vamos a adaptar, analizar, explicar y optimizar para que puedas usarlo. Voy a emular la conocidísima API IsDebuggerPresent, que yo creo que no necesita presentación.
Código asm de IsDebuggerPresent
Aunque voy a generar el código asm a mano, yo prefiero (prefería) hacerlo usando un complemento para VB6 que en el siguiente tutorial mostraré, pero esta es una rápida alternativa. Lo único malo es que necesitas los valores en hexadecimal del código ensamblador pero te voy a enseñar cómo obtenerlos en menos de 1 segundo.
Primer paso: obtención del código
Por ejemplo, vamos a programar algo muy sencillo para entenderlo y que nos sea útil. Voy a abrir en OllyDBG (v1.10 o v2) un programa cualquiera. Cuando esté cargado pulso Ctrl+G y voy a poner exactamente IsDebuggerPresent que es una API que comprueba si un debugger está depurando nuestra aplicación. El código que encuentro es el siguiente:
7C813123 64:A1 18000000 mov eax,dword ptr fs:[18] 7C813129 8B40 30 mov eax,dword ptr ds:[eax+30] 7C81312C 0FB640 02 movzx eax,byte ptr ds:[eax+2] 7C813130 C3 retn
Las direcciones 7C81XXXX seguramente no se corresponderán con las tuyas pero da igual. OllyDBG ya muestra todo lo que realmente interesa: las instrucciones y el código hexadecimal. Si al ejecutar IsDebuggerPresent el resultado final de eax es:
- 0 -> No hay debugger
- 1 -> Sí hay debugger
Voy a copiar separados por un espacio todos los valores en hexadecimal de las instrucciones:
64 A1 18 00 00 00 8B 40 30 OF B6 40 02 C3 en total 14 bytes.
Obtener el valor hex desde OllyDBG
Como puedes imaginar, si el código fuera extenso, copiar todos los valores te puede llevar trabajo y posibles equivocaciones. Para evitar esto, puedes copiar los datos hex directamente desde OllyDBG:
Los números 1 y 2 corresponden a OllyDBG 1.10 mientras que los números 3 y 4 a OllyDBG 2. Lo primero es seleccionar el código 1, 3 y después pulsar el botón derecho y seleccionar los ítems 2 y 4 dependiendo de la versión de Olly.
Generando la subrutina o función en asm
Y ahora ya podemos empezar con el código que usaremos en Visual Basic 6. De forma muy general, lo que voy a hacer es crear una función (o subrutina si quieres, pero como IsDebuggerPresent devuelve 0 o 1 voy a crear un función con resultado booleano) para generar y ejecutar todo el código asm y lo que hará esta función es pegar esos valores hex en memoria uno a uno y después mediante CallWindowProc generaremos un procedimiento para que el código salte a la dirección de comienzo del asm. Vamos a ello. Crearé un esqueleto que te puede servir para tu propio programa.
Crea un nuevo proyecto en VB6, un exe estándar. Dibuja la interfaz como quieras pero añade un botón que será el que ejecute código asm. Yo al formulario lo he llamado "principal" y al botón "btn_ejecutar". La función la voy a llamar asm_IsDebuggerPresent, que voy a insertar en el código general del formulario:
Private Function asm_IsDebuggerPresent() As Boolean
End Function
Ahora crearemos una matriz asm() con todos los valores hexadecimales y la función CallWindowProc. Voy a usar una (constante-1)=bytes_codigo, para crear el array. La función asm_IsDebuggerPresent queda así:
Private Function asm_IsDebuggerPresent() As Boolean Dim asm(bytes_codigo) As Byte Dim indice As Integer Dim codigo_asm, bytes() As String codigo_asm = "64 A1 18 00 00 00 8B 40 30 0F B6 40 02 C3" bytes = Split(codigo_asm, " ", -1, vbTextCompare) For indice = 0 To UBound(asm) bytes(indice) = "&H" & bytes(indice) 'Se quedan en Unicode asm(indice) = CByte(bytes(indice)) 'Los transforma en bytes Next indice 'Ejecutar el código desde el principio VarPtr(asm(0). La función retorna un valor Long 'por eso le obligo a devolver un valor Booleano y que nos sirva en nuestra función: asm_IsDebuggerPresent = CBool(CallWindowProc(VarPtr(asm(0)), 0&, 0&, 0&, 0&)) End Function
Y todo el código final queda así:
'Declaración de la función CallWindowProc: Private Declare Function CallWindowProc Lib "user32.dll" Alias "CallWindowProcA" _ (ByVal lpPrevWndFunc As Long, _ ByVal hwnd As Long, _ ByVal msg As Long, _ ByVal wParam As Long, _ ByVal lParam As Long) As Long 'Constante con el número exacto de bytes - 1: Const bytes_codigo = 13 'Función que genera y ejecuta el código ensamblador: Private Function asm_IsDebuggerPresent() As Boolean Dim asm(bytes_codigo) As Byte Dim indice As Integer Dim codigo_asm, bytes() As String codigo_asm = "64 A1 18 00 00 00 8B 40 30 0F B6 40 02 C3" bytes = Split(codigo_asm, " ", -1, vbTextCompare) For indice = 0 To UBound(asm) bytes(indice) = "&H" & bytes(indice) 'Se quedan en Unicode asm(indice) = CByte(bytes(indice)) 'Los transforma en bytes Next indice 'Ejecutar el código desde el principio VarPtr(asm(0). La función retorna un valor Long pero 'le obligo a devolver un valor Booleano y que nos sirva en nuestra función: asm_IsDebuggerPresent = CBool(CallWindowProc(VarPtr(asm(0)), 0&, 0&, 0&, 0&)) End Function Private Sub btn_ejecutar_Click() Dim eax As Boolean eax = asm_IsDebuggerPresent If eax = True Then MsgBox "Debugger detectado", vbExclamation, "IsDebuggerPresent" Else MsgBox "Debugger no detectado", vbInformation, "IsDebuggerPresent" End If End Sub
Muy sencillo de entender y optimizado para que lo puedas modificar y utilizar de forma sencilla. Iba a incluir el proyecto y el programa compilado, pero creo que con el código y la explicación basta. Si realmente lo crees necesario coméntalo.
Quiero dar las gracias desde aquí a Karcrack, usuario de elhacker que fue el que realizó el código original (el enlace del principio de este artículo) y que yo he copiado y adaptado.