Bueno no espero que esto sea una enciclopedia api, pero espero que esto pueda ser el
primer paso para empezar a usar apis en VB de muchos y dejar de lado el tedioso trabajo
de llevar archivos .OCX a cada lugar donde llevamos nuestra aplicación. Además en mi
caso el haber usado mucho las apis desde VB6 me facilito muchísimo pasar a
MASM32 desde el tasm para ms-dos.... bla bla bla ya parezco una vieja...
como dijeron The Ramones jei jou! letsgou! ;)
***********************************************************************************************
PRIMER EJEMPLO: Un ejemplo sencillo
***********************************************************************************************
ACLARACION!!!: todo los ejemplos están hechos en VB6
1) creamos un nuevo proyecto con un formulario Form1 y copiamos el siguiente código
'<el codigo empieza justo despues de esta etiqueta>
Option Explicit
Private Declare Function MessageBox Lib "user32" Alias "MessageBoxA" (ByVal hwnd As Long, _
ByVal lpText As String, _
ByVal lpCaption As String, _
ByVal wType As Long) As Long
Private Sub Form_Load()
MessageBox Me.hwnd, "PRIMER EJEMPLO DE APIS HECHO POR ACHERNAR", "Ejemplo API", 0
End Sub
'<el codigo termino justo antes de esta etiqueta>
2) listo... le damos F5 para ejecutarlo y voila... un msgbox con un texto hermoooossssso
¿no? a mi me parece muy lindo y esta hecho con una sencilla api... que no te gusta, hey
no me presiones que estoy ebrio y puedo llegar a decir cosas como que detrás de la simpleza
de una margarita se haya todo el poder de la naturaleza, pero no te lo voy a decir :P
Bueno dejo mi vaso y vamos a analizar un poco esto
Primero tenemos lo de "Private Declare Function MessageBox..." acá estamos avisando que
función vamos a usar y en que librería esta (user32 --> user32.dll dentro de System32),
si usamos Alias quiere decir que dentro de la dll la función se llama MessageBoxA, pero
nosotros la vamos a llamar MessageBox para que VB sepa que estamos diciendo cuando llamemos
a una función MessageBox.
Este alias por ejemplo se usa en la función Winsock select para verificar el estado de
un socket si esta en alguna combinacion de lectura/escritura/error. Como No se puede
usar una función select en vb (porque es una palabra reservada "Select Case") se la
puede llamar vbSelect
Volviendo a MessageBox vemos que a la derecha esta los parametros hwnd, lpText,
lpCaption y WType
hwnd: este es el MANEJADOR (o handle) de la ventana padre (la que crea la ventanita msgbox hija).
en este ejemplo la ventana padre seria el form1. En windows las ventanas son creadas en
algún momento (no existe una ventana dios que existe y existirá por siempre... a menos
que Bill nos haya ocultado algo), bueno y cuando se crea una ventana, esta tiene un
numero que la identifica como cuando creamos un archivo algo así:
Open "archivo.txt" for Random as #1 Len = Len(Registro)
Put #1,,Registro
close #1
puede verse que para grabar un registro en el archivo o cerrarlo no usa "archivo.txt"
para referirse al archivo, sino el numero #1 que es el MANEJADOR del archivo
Volviendo al ejemplo del msgbox... este argumento hwnd puede ser nulo indicando que el
msgbox no tiene ventana padre (no genera ningún problema).
lpText: este argumento es el mensaje propiamente dicho, en vb se pasa una cadena, pero vale
aclarar este detalle: si ven el nombre de la variable comienza con lp eso se usa en
otros lenguajes (como masm32) para destacar que dicha variable es un puntero y en dichos
lenguajes no se pasa una cadena, sino la dirección de memoria donde esta guardada la
cadena, en este caso, en VB ,se facilita bastante, pero muchas veces hay problemas con las
apis porque VB no permite manipular tan bien la memoria como C++ o MASM32 entonces hay
que recurrir a ciertos artilugios.
lpCaption: similar al anterior fíjense que también empieza con lp, es un puntero, pero nosotros
solo pasamos una cadena y listo... casi me olvido, este es el titulo de la ventana
wType: esto indica el tipo de ventana msgbox... o sea.. con solo un botón OK o Yes/NO
o Yes/No/Cancel etc el valor cero = solo OK, podemos experimentar agregando las
siguientes constantes a nuestro proyecto anterior y sustituyéndolas por el ultimo termino
Private Const MB_OK = 0
Private Const MB_OKCANCEL = 1
Private Const MB_ABORTRETRYIGNORE = 2
Private Const MB_YESNOCANCEL = 3
Private Const MB_YESNO = 4
Private Const MB_RETRYCANCEL = 5
luego la llamada a la función podría quedar algo así
MessageBox Me.hwnd, "Este Es El Primer Ejemplo", "Ejemplo API", MB_ABORTRETRYIGNORE
Bueno como al primer ejemplo de api lo llevamos fácil como nene para la escuela, antes de
que se enfríen las articulaciones vamos a meter un ejemplo un poco mas elaborado pero facilito
***********************************************************************************************
SEGUNDO EJEMPLO: Un ejemplo mas elaborado ;)
***********************************************************************************************
lo que vamos a hacer acá es un programa para cambiarle el titulo a otra ventana cualquiera
si... podes hacer eso... a cualquier programa le podes hacer lo que quieras... como dijo
el DR. Octopus en SpiderMan II "The power of the sun in the palm of my hand"
1) lo de siempre creamos un proyecto con un formulario Form1, un botón Command1 y un
timer Timer1 y le copiamos el siguiente código
'<el codigo empieza justo despues de esta etiqueta>
Option Explicit
'para este ejemplo se necesita un form1 con un command1 y un timer1
'con las siguientes 5 apis nos va a alcanzar para este ejemplo
'la funcion MessageBox crea un msgbox
'Esta funcion esta dentro de User32.dll que esta dentro de la carpeta System32
Private Declare Function MessageBox Lib "user32" Alias "MessageBoxA" (ByVal hwnd As Long, _
ByVal lpText As String, _
ByVal lpCaption As String, _
ByVal wType As Long) As Long
'con la funcion WinExec vamos a poder ejecutar un programa
'lpCmdLine es el path del programa (tendria que ser un puntero,
' pero para nosotros es mas facil)
' como voy a ejecutar el notepad.exe creo que alcanza poner
' "notepad.exe" porque esta dentro de la carpeta System32
'nCmdShow es el modo en que se va a mostrar la pantalla
' 0 = hide, o sea, la ventana esta pero no se ve
' 1 = cuando no esta entre minimizado y maximizado... no se como se llama :P
' 2 = minimizado
' 3 = maximizado
'Esta funcion esta dentro de Kernel32.dll que esta dentro de la carpeta System32
Private Declare Function WinExec Lib "kernel32" (ByVal lpCmdLine As String, _
ByVal nCmdShow As Long) As Long
'con la funcion Beep puede emitirse una señal sonora de cierta duracion y frecuencia
'por el diminuto parlantito que hay dentro de la cpu (no importa que bajes el volumen
'a ese no lo bajas... no se ocurra usar esto para hacer programas para fastidiar a
'la gente con bucles infinitos!! XDDD )
'dwFreq es la frecuencia en Hertz (Ciclos por segundo)
'dwDuration es la duracion en milisegundos
'Esta funcion esta dentro de Kernel32.dll que esta dentro de la carpeta System32
Private Declare Function Beep Lib "kernel32" (ByVal dwFreq As Long, _
ByVal dwDuration As Long) As Long
'la funcion GetForegroundWindow nos devuelve el MANEJADOR de la ventana que se
'encuetra sobre toda las demas... foreground window. En el ejemplo anterior explicaba
'un poco sobre el manejador de las ventanas suele llamarse tambien hWnd.
'Esta funcion no necesita argumentos
'Esta funcion esta dentro de Kernel32.dll que esta dentro de la carpeta System32
Private Declare Function GetForegroundWindow Lib "user32" () As Long
'con la funcion SetWindowText se puede modificar el texto de una ventana, los botones
'tambien son ventanas... tienen manejadores como cualquier ventana, los textbox, los
'listboxs y los checkboxs tambien son ventanas y muchos mas tambien todos estos elementos
'puedes ser manipulados y modificados...
'Esta funcion esta dentro de Kernel32.dll que esta dentro de la carpeta System32
Private Declare Function SetWindowText Lib "user32" Alias "SetWindowTextA" (ByVal hwnd As Long, _
ByVal lpString As String) As Long
Private CuentaSegundos As Integer
Private Sub Form_Load()
Timer1.Enabled = False
Timer1.Interval = 1000 'llama a la funcion del tiemer cada un segundo
CuentaSegundos = 0
End Sub
Private Sub Command1_Click()
'al apretar el boton, abre el block de nota
'este aparece sobre todas las demas ventanas (ForeGroundWindow)
'y comienza a ejecutarse el timer para luego de 3 segundos
'buscar la ventana que se encuentra sobre todas y cambiarle el titulo
WinExec "notepad.exe", 3
Timer1.Enabled = True
End Sub
Private Sub Timer1_Timer()
Dim Manejador As Long
'emite un sonido de 500 Hz durante 200 miliSegundos
Beep 500, 200
CuentaSegundos = CuentaSegundos + 1
If CuentaSegundos = 5 Then
Manejador = GetForegroundWindow
SetWindowText Manejador, "ESTE ES EL SEGUNDO EJEMPLO DE APIS HECHO POR ACHERNAR"
MessageBox Me.hwnd, "El Texto ha sido modificado", "Boom!!", 0
Timer1.Enabled = False
End If
End Sub
'<el codigo termino justo antes de esta etiqueta>
2) le damos F5 para ejecutar... si hacemos click en el botón se abre el block de notas
y cada segundo va a sonar un beep, luego de 5 segundos (5 beeps) el titulo de la
ventana del block de notas se va a modificar por otro texto.
esto me hace acordar un programa que hice y fue como me di cuenta que la fuerza bruta no
no ayuda de mucho... era un programa de fuerza bruta para un programa que se llama
UniversalShield y sirve para guardar archivos y carpetas bajo un password. Cuando el
UniversalShield pide el pass no hace como otros que luego de unos errores cierra la ventana,
lo cual demoraría la fuerza bruta así que no era muy difícil de programar. Otra cosa era que
el botón ok del UniversalShield para aceptar el pass estaba deshabilitada y se habilitaba cuando
se escribía el pass correcto.
Mi programa tenia 256 o 255 checkboxs con distintos símbolos ascii, de ahí elegía los que me
parecía que podría contener el pass, por ejemplo solo los 10 numero, ingresaba el caption de
la ventana que me pedía el pass. El programa buscaba una ventana (acá ya se mostró como buscar
la foreground window) con el caption ingresado. En esa ventana buscaba el textbox y le mandaba
pulsaciones de teclas sintetizadas (también se puede simular con apis) cada vez que el programa
tecleaba un posible pass verificaba si el botón estaba enabled o disabled (también se puede
hacer con apis) y si el botón estaba enabled (habilitado) era que había encontrado el password
Era un programa muy pobre pero era bueno para practicar el uso de apis y en esa época estaba
viendo los mensajes entre ventanas y era el modo en que sintetizaba las pulsaciones sobre
el textbox.
Al igual que se usan estas funciones, windows tiene infinidad (bueno, no lo tomes literal...
muchas, muchisimas) funciones para hacer una cantidad impresionante de cosas. Todo lo que hacen
los archivos OCX es llamando a apis asi que es posible sustituir estos archivos por código
hecho por nosotros con llamadas a apis. Lo que voy a explicar es como usar apis para evitar
el uso del Microsoft winsock Control 6.0 (MSWINSCK.OCX) con no mas de 11 apis WOW!!!! XDD
***********************************************************************************************
UN POCO DE TEORIA EMINENTEMENTE PRACTICA ¿? ;)
(la música de fondo es "Children of the Grave" de Black Sabbath)
***********************************************************************************************
Acá es donde la cosa se complica... porque yo estudio Ingeniería Mecánica XDDDD
Mejor no me voy a poner a explicar que es un socket porque sino la cago :P supongo que si
alguien venia usando el control winsock de VB debe conocer algo de esto... personalmente conocí
winsock vía api y nunca use dicho control :/ .Todas estas funciones explicadas de acá en
adelante están en wsock32.dll que esta dentro de la carpeta System32
Creo que lo voy a explicar mejor con un diagrama
así funciona un cliente:
1) iniciamos una sesión Winsock (avisamos que vamos a usar winsock)
Esto se hace con la funcion WSAStartp
2) creamos un socket (digamos una terminal de entrada y salida de datos)
Esto se hace con la funcion soket
3) conectamos el socket a una dirección remota en un cierto puerto
Esto se hace con la función Connect, pero son necesarias un par
de funciones mas para condimentar un poco el asunto
4) si la conexión se realizo ya se pueden enviar mensajes, pero
también hay que verificar si hay mensajes entrantes para leer
Leer se hace con la función recv
Escribir se hace con la función send
5) para terminar se puede cerrar el socket y terminar la sesión winsock
o directamente terminar la sesion winsock y esta elimina todos los
sockets creados por el proceso que llame a esta función
Un socket se cierra con la función closesocket
Una sesión se termina con WSACleanup
Un servidor es levemente distinto, el paso 3 no es conectarse, sino poner el socket en modo escucha
para esto se usa la función bind para enlazar el socket con la IP local y el puerto donde se quiere
dar el servicio y luego la función listen para poner el socket a la espera de una conexión
A explicar un poco como funcionan estas funciones
("FUNCINAN ESTAS FUNCIONES" que recursivo!!!XDDD ah a propósito de esto leí que la RAE no acepta este
termino tan utilizado en informática, por ejemplo GNU es un acrónimo recursivo o sea una especie de
sigla que hace referencia a si misma --> GNU = GNU is Not Unix, la RAE propone otra palabra para
sustituir por recursivo que ahora no recuerdo)
* WSAStartup: (avisa que este proceso inicia una sesión winsock)
Declare Function WSAStartup Lib "WSOCK32" (ByVal wVersionRequired As Long, _
lpWSADATA As WSAData) As Long
wVersionRequired es la versión mas alta que soporta quien llama a esta función y esta se pasa
teniendo en cuenta lo siguiente supongamos la versión ficticia v5.7, 5 es la
versión y 7 la revisión, bueno como las variables que empezaban con lp eran
punteros ahora puede verse que wVersionRequired empieza con w es un word, o
sea, un par de bytes de los cuales el de mayor orden va a representar la
revisión y el de menor orden la versión casi nunca vas a tener error si usas
la versión de de Windows Sockets 2.2 o sea pasarle el valor &H202 como
primer argumento a esta función
lpWSADATA Seria un puntero a una estructura tipo WSAData, pero como en VB es mas fácil
directamente pasamos el nombre de la estructura
este tipo se declara de este modo
Type WSAData
wVersion As Integer
wHighVersion As Integer
szDescription As String * 257
szSystemStatus As String * 129
iMaxSockets As Integer
iMaxUdpDg As Integer
lpVendorInfo As Long
End Type
Generalmente todo esto no nos va a importar mucho.. digo, si estas por crear un cliente de
IRC solo queres iniciar winsock y nada más
Si la llamada a esta función es satisfactoria, el valor de retorno será CERO
* WSACleanup: (avisa que este proceso termina una sesión winsock)
Declare Function WSACleanup Lib "WSOCK32" () As Long
no tiene argumentos
* socket: (crea un socket)
Declare Function socket Lib "wsock32.dll" (ByVal af As Long, _
ByVal s_type As Long, _
ByVal protocol As Long) As Long
af es AddressFamily (la familia de direccion)
s_type es el tipo de socket
protocol es el protocolo que operara con el socket
valores comunes (troyanos, bots, clientes para keyloggers remotos, no se.. cosas comunes) son
af = AF_INET = 2
s_type = SOCK_STREAM = 1
protocol = IPROTO_TCP = 6
Si la llamada a la función es satisfactoria retorna un valor mayor a cero que es el manejador
del socket (ese valor para operar sobre el socket como con los archivos y las ventanas) y si
hay algún error y no se puede crear retorna -1
* closesocket: (cierra un socket)
Declare Function closesocket Lib "wsock32.dll" (ByVal s As Long) As Long
s es el manejador del socket que se quiere cerrar
ahora viene una explicación peque peque para la conexión
esta es la declaración de la función connect, la que establece la conexión, así
esta en la Api Guide (después voy a dar la dirección para que busque quien quiera la api
que quiera... también toda la info sobre esto esta en el msdn de microsoft pasen por ahí que
siempre atienden bien con café, torta, constantes, estructuras, funciones y códigos fuente ;)
Declare Function Connect Lib "wsock32.dll" Alias "connect" (ByVal s As Long, addr As sockaddr, _
ByVal namelen As Long) As Long
que tenemos... s = manejador del socket (ya usamos manejadores como si en lugar de nombres
tuviesemos manejadores no?)
despues... addr = es otra estructura... muchas veces se usan estructuras con las apis tanto
para pasar info a la función como para recibir info. A veces esta info no
es importante y por ejemplo en masm32 suelo crear un buffer sin nada y
y en lugar de declarar tantas estructuras directamente paso la dirección
de memoria del buffer... dirección de memoria... mmm si!! punteros!!!
esta estructura se declara de este modo
Type sockaddr
sin_family As Integer
sin_port As Integer
sin_addr As Long
sin_zero As String * 8
End Type
en sin_family va la familia del socket, ¿como que socket? el que vamos a conectar, el que creamos
con la función socket mas arriba y como argumento AddressFamily le pasamos 2 que es AF_INET, acá
también. Que dolores de cabeza me estas dando niño!!! tu madre que dice de todo esto???!!! XD!
en sin_port va el puerto, pero acá empieza a haber algunos detalles extras, porque si te vas a
conectar al puerto 6667 por ejemplo de un servidor de IRC no hay que pasar el numero así como así.
hay ciertos tipos de ordenamientos de bytes (byte order) uno es el de red (network) y el otro
es el de host (JOST XDDD, lo de este paréntesis es broma no se confundan)
para pasar de ordenamiento de red al de host se usa la función ntohs y para lo opuesto htons
hey siempre me habían parecido letras a azar pero miren ntohs = Network to Host.. y la s? :S
nosotros vamos a hacer que estos datos viajen a través de la red para hacer una petición de
conexión y tenemos que cambiar el ordenamiento a NetWork con htons, pero antes viene uno de esos
problemitas con el tamaño de la variable que no sucede en otros lenguajes, antes de usar la
función htons hay que modificar el valor del puerto del siguiente modo
If Not Valor <= 32767 Then
Valor = Valor - 65536
End If
o sea la función para cambiar el ordenamiento podría ser así (sin la declaración de htons)
Private Function CambiarOrdenamiento(ByVal Valor As Long) As Integer
If Not Valor <= 32767 Then
Valor = Valor - 65536
End If
CambiarOrdenamiento = htons(Valor)
End Function
continuando con la estructura sockaddr tenesmos la variable sin_addr, para la cual también
hay que hacer ciertas cositas. Como se sabe una maquina en una red puede ser referida mediante
un Hostname o una direccion IP, el primero es algo como achernar.com y el segundo es algo como
207.46.104.20.
1) partiendo de una ip: suponiendo 207.46.104.20 -->
--> sin_addr = 20 * 256^0 + 104 * 256^1 + 46 * 256^2 + 207 * 256^3
2) partiendo de un hostname:
aca hay que usar una api para manipular memoria (RtlMoveMemory) esta funcion guarda en una
variable, una cierta cantidad de bytes copiados a partir de una direccion de memoria. Vamos
a tener que usar esto porque hay funciones que retornan punteros y ahí es donde los VBoys estamos
medio en p#lotas.
la función gethostbyname toma una cadena con el hostname y retorna un puntero a una estructura
HOSTENT, o sea los datos que queremos estan en alguna parte de la memoria y esta función nos
da dicha dirección... después con RtlMoveMemory copiamos estos datos a una variable para poder
manipular mejor estos datos.
la estructura HOSTENT es esta
Private Type HOSTENT
Nombre As Long
Alias As Long
TipoDeDireccion As Integer
Longitud As Integer
ListaDeDirecciones As Long
End Type
en ListaDeDirecciones hay un puntero (una dirección de memoria) que apunta a un lugar donde
hay otro puntero mas... si es una locura... y este ultimo puntero apunta a un lugar
de la memoria donde esta la IP del host que estamos buscando lista para guardar en
la variable sin_addr de la estructura sockaddr porque es para eso que estamos haciendo esto..
ya te habías olvidado... no te culpo... todo esto es para poder hacer una simple conexión TCP
A causa de tener que manipular tantos punteros se usa varias veces RtlMoveMemory.
NOTA: creo que, no estoy muy seguro, gethostbyname puede tomar también cadenas tipo IP
(xxx.xxx.xxx.xxx) pero quería mostrar la transformación por potencias que puse antes.
(a la IP 127.0.0.1 si la acepta perfectamente)
volviendo a la estructura sockaddr queda ver el ultimo termino que es sin_zero y esto es fácil
sencillamente no se toca... porque esta reservado.
las declaraciones de estas funciones son las siguientes
Declare Function Connect Lib "wsock32.dll" Alias "connect" (ByVal s As Long, _
addr As sockaddr, _
ByVal namelen As Long) As Long
Declare Function htons Lib "wsock32.dll" (ByVal hostshort As Long) As Integer
Declare Function gethostbyname Lib "WSOCK32" (ByVal szHost As String) As Long
Private Declare Sub RtlMoveMemory Lib "kernel32" (hpvDest As Any, _
ByVal hpvSource As Long, _
ByVal cbCopy As Long)
con esto ya podemos conectarnos por ejemplo a un servidor telnet... o por ejemplo cuando el
netcat da una shell directa (no inversa) nos podemos conectar y hacernos una interfaz con letras
tipo Papyrus (me encanta, me imagino revisando el correo en una carabela :P )
ahora luego de conectarnos tenemos que tener algún método para ver si hay algún mensaje entrante
o si el host remoto se desconecto o si podemos escribir Hello Internet!
la función que se utiliza para esto es select
* select: ( retorna el estado del socket en cuestión [lectura/escritura/error] )
Declare Function vbselect Lib "ws2_32.dll" Alias "select" (ByVal nfds As Long, _
ByRef readfds As Any, _
ByRef writefds As Any, _
ByRef exceptfds As Any, _
ByRef timeout As Long) As Long
esta función se usa de la siguiente manera...
primero los 3 argumentos declarados tipo any son 3 estructuras fd_set que son así
Private Type fd_set
Contador As Long 'Cuantos sockets están en este estado?
Arreglo(1 To 64) As Long 'Arreglo con los manejadores en cuestión.
End Type
son 3 estructuras una para información de lectura otra para escritura y otra para error
cuando llamamos la función en el arreglo de cada una de estas estructuras metemos el manejador
de nuestro socket (supongo que se pueden meter mas) y después de llamar la función leemos
los contadores de estas estructuras... si el contador de la estructura de escritura esta en
1 (uno) es que hay datos para leer en el socket, si esta en 0 (cero es que no hay nada para
leer) igual con la estructura de escritura o error.
esta función me ha dado buenos resultados con error... cuando el host remoto se desconecta
me responde como si tuviera un mensaje para leer, pero con la diferencia que cuando lo leo
me dice que la longitud del mensaje es cero bytes... así que así hago para ver si se terminó
la conexión con el host remoto.
a los otros dos parámetro que quedan para llamar a esta función les pasamos cero como valor
bueno ahora tenemos una conexión y sabemos fehacientemente que tenemos un mensaje para leer
como hacemos? ahora lo explico, pero esperen unos minutos que tengo un llamado urgente de la
naturaleza que no puedo eludir..................
...
...
..
.
..
.
...
.
listo... uhh que alivio :P
bueno acá vamos a terminar con la recepción y envío de datos con send y recv
* recv: (recibe la data y la guarda en un buffer)
Declare Function recv Lib "wsock32.dll" (ByVal s As Long, _
buf As Any, _
ByVal buflen As Long, _
ByVal flags As Long) As Long
s = el manejador del socket
buf = un buffer, una matriz tipo byte que yo suelo usar de 1 a 8192
buflen = tamaño del buffer... 8192 bytes
flags = se pone a cero
si todo sale bien retorna un valor que es la cantidad de bytes leídos, si retorna
cero hubo algún error y ya explique que es como yo verifico si se corto la conexión
con el host remoto. fácil no?
hay un detallito... como la data la recibimos en un buffer tipo bytes hay que pasarla
a una cadena de texto... y se hace asi
StringBuffer = StrConv(ByteBuffer, vbUnicode)
StringBuffer = Left$(StringBuffer, BytesRecibidos)
acá no hay ninguna api StrConv, Left$ y vbUnicode son dos funciones y una constante
todas de VB6
ufff y acá tomamos la recta final.... de esta TEORIA EMINENTEMENTE PRACTICA
* send: (envía datos al host remoto tomándolos de un buffer)
Declare Function Send Lib "wsock32.dll" Alias "send" (ByVal s As Long, _
buf As Any, _
ByVal buflen As Long, _
ByVal flags As Long) As Long
funciona igual que send
s = el manejador del socket
buf = un buffer, una matriz tipo byte que yo suelo usar de 1 a 8192, pero ahora
tomamos datos de este buffer....
buflen = tamaño del buffer... 8192 bytes
flags = se pone a cero
listo ya esta todo explicado como para hacer un cliente en una conexión TCP
el siguiente es el código que debe ponerse en un modulo de VB y se agrega a cada
proyecto que se quiera usar winsock y así no usamos fastidiosos OCX's
'<el codigo empieza justo despues de esta etiqueta>
Option Explicit
'Funciones PUBLICAS en este modulo:
'
'IniciarSesionSocket
'TeminarSesionSocket
'CrearSocket
'CerrarSocket
'Conectar
'Recibir
'Enviar
'EstadoSocket <-- no da buenos resultados
'MensajeEntrante
'
'(NOTA: estan declaradas en el orden mostrado)
'
'Ademas hay una variable publica booleana que indica si winsock fue iniciado
'(Public WinsockIniciado As Boolean)
'
Private Declare Function WSAStartup Lib "ws2_32.dll" (ByVal wVersionRequired As Long, _
lpWSAData As WSAData) As Long
Private Declare Function WSACleanup Lib "ws2_32.dll" () As Long
Private Declare Function Socket Lib "ws2_32.dll" Alias "socket" (ByVal Familia As Long, _
ByVal Tipo As Long, _
ByVal Protocolo As Long) As Long
Private Declare Function closesocket Lib "ws2_32.dll" (ByVal s As Long) As Long
Private Declare Function htons Lib "ws2_32.dll" (ByVal hostshort As Integer) As Integer
Private Declare Function gethostbyname Lib "ws2_32.dll" (ByVal host_name As String) As Long
Private Declare Sub RtlMoveMemory Lib "kernel32" (hpvDest As Any, _
ByVal hpvSource As Long, _
ByVal cbCopy As Long)
Private Declare Function connect Lib "ws2_32.dll" (ByVal s As Long, _
ByRef name As sockaddr_in, _
ByVal namelen As Long) As Long
Private Declare Function recv Lib "wsock32.dll" (ByVal s As Long, _
buf As Any, _
ByVal buflen As Long, _
ByVal flags As Long) As Long
Private Declare Function send Lib "ws2_32.dll" (ByVal s As Long, _
ByRef buf As Any, _
ByVal buflen As Long, _
ByVal flags As Long) As Long
Private Declare Function vbselect Lib "ws2_32.dll" Alias "select" (ByVal nfds As Long, _
ByRef readfds As Any, _
ByRef writefds As Any, _
ByRef exceptfds As Any, _
ByRef timeout As Long) As Long
Private Type WSAData
Version As Integer
MayorVersion As Integer
Descripcion As String * 257
EstadoDelSistema As String * 129
MaximosSockets As Integer
MaximaUPDData As Integer
InfoDelVendedor As Long
End Type
Private Type HOSTENT
Nombre As Long
Alias As Long
TipoDeDireccion As Integer
Longitud As Integer
ListaDeDirecciones As Long
End Type
Private Type sockaddr_in
Familia As Integer
Puerto As Integer
Direccion As Long
Cero(1 To 8) As Byte
End Type
Private Type fd_set
Contador As Long 'Cuantos sockets estan en este estado?
Arreglo(1 To 64) As Long 'Arreglo con los manejadores en cuestion.
End Type
'esta es una bandera usada para cuando se llama a TeminarSesionSocket
'para saber si la sesion fue iniciada o no (en tal caso no abria que terminar nada)
Private WinsockIniciado As Boolean
'*****************************************************************
'*****************************************************************
'*****************************************************************
Public Function IniciarSesionSocket() As Boolean
'IniciarSesionSocket por ACHERNAR 7/2007
'para el foro de www.elhacker.net
'
'La funcion inicia una sesion socket
'si todo sale bien retorna True
'Si hay error retorna False
Dim MiEstructuraWinsockData As WSAData
Dim Retorno As Long
Retorno = WSAStartup(&H202, MiEstructuraWinsockData)
If Retorno = 0 Then
WinsockIniciado = True
IniciarSesionSocket = True
Else
WinsockIniciado = False
IniciarSesionSocket = False
End If
End Function
Public Function TeminarSesionSocket()
'TeminarSesionSocket por ACHERNAR 7/2007
'para el foro de www.elhacker.net
'
'Termina sesion Winsock
If WinsockIniciado Then
WSACleanup
End If
End Function
'*****************************************************************
'*****************************************************************
'*****************************************************************
Public Function CrearSocket() As Long
'CrearSocket por ACHERNAR 7/2007
'para el foro de www.elhacker.net
'
'La funcion abre un socket AF_INET , SOCK_STREAM, IPROTO_TCP
'si no hay errores retorna un manejador del socket > 0,
'si hubo errores retorna cero
CrearSocket = Socket(2, 1, 6)
If CrearSocket <= 0 Then
CrearSocket = 0
End If
End Function
Public Function CerrarSocket(ByVal ManejadorSocket As Long) As Boolean
'CerrarSocket por ACHERNAR 7/2007
'para el foro de www.elhacker.net
'
'La Funcion cierra un socket abierto
'Si todo sale bien retorna True
'Si hay error retorna False
If Not closesocket(ManejadorSocket) Then
CerrarSocket = True
Else
CerrarSocket = False
End If
End Function
'*****************************************************************
'*****************************************************************
'*****************************************************************
Private Function CambiarOrdenamiento(ByVal Valor As Long) As Integer
'CambiarOrdenamiento por ACHERNAR 7/2007
'para el foro de www.elhacker.net
'
If Not Valor <= 32767 Then
Valor = Valor - 65536
End If
CambiarOrdenamiento = htons(Valor)
End Function
'*****************************************************************
'*****************************************************************
'*****************************************************************
Private Function ObtenerIPDelNombre(ByVal Nombre As String) As Long
'ObtenerIPDelNombre por ACHERNAR 7/2007
'para el foro de www.elhacker.net
'
Dim PunteroAEstructura As Long
Dim PunteroAUnaIP As Long
Dim MiEstructuraHOSTENT As HOSTENT
Dim DireccionIP As Long
PunteroAEstructura = gethostbyname(Trim$(Nombre))
If PunteroAEstructura = 0 Then
DireccionIP = 0
Else
RtlMoveMemory MiEstructuraHOSTENT, PunteroAEstructura, LenB(MiEstructuraHOSTENT)
'
'ahora MiEstructuraHOSTENT.ListaDeDirecciones contiene un arreglo de direccion IP
'y es necesario obtener un puntero a la primera IP del arreglo
'
RtlMoveMemory PunteroAUnaIP, MiEstructuraHOSTENT.ListaDeDirecciones, 4
If Not PunteroAUnaIP = 0 Then
'
'Paso los valores de la IP a una variable Long
'
RtlMoveMemory DireccionIP, PunteroAUnaIP, MiEstructuraHOSTENT.Longitud
'
Else
DireccionIP = 0
End If
End If
ObtenerIPDelNombre = DireccionIP
End Function
'*****************************************************************
'*****************************************************************
'*****************************************************************
Public Function Conectar(ByVal ManejadorSocket As Long, _
ByVal NombreHost_IP As String, _
ByVal PuertoRemoto As Long, _
IP As Boolean) As Long
'Conectar por ACHERNAR 7/2007
'para el foro de www.elhacker.net
'
'Esta funcion realiza una conexión a un host remoto
'si el argumento ip es true es que el argumento NombreHost_IP contiene una ip
'si el argumento ip es false es que el argumento NombreHost_IP contiene el
'nombre del host
'Si todo sale bien retorona True
'Si hay error retorna False
Dim EstructuraDireccion As sockaddr_in
Dim Posicion1 As Integer
Dim Posicion2 As Integer
Dim IpNumero As Long
Dim i As Integer
If IP = True Then
Posicion1 = 1
IpNumero = 0
For i = 1 To 4
Posicion2 = InStr(Posicion1, NombreHost_IP, ".", vbBinaryCompare)
If Posicion2 = 0 Then
Posicion2 = Len(NombreHost_IP) + 1
End If
IpNumero = IpNumero + CLng(Mid(NombreHost_IP, Posicion1, _
Posicion2 - Posicion1)) * 256 ^ (i - 1)
Posicion1 = Posicion2 + 1
Next i
EstructuraDireccion.Direccion = IpNumero
Else
EstructuraDireccion.Direccion = ObtenerIPDelNombre(NombreHost_IP)
End If
EstructuraDireccion.Familia = 2
EstructuraDireccion.Puerto = CambiarOrdenamiento(PuertoRemoto)
If connect(ManejadorSocket, EstructuraDireccion, Len(EstructuraDireccion)) Then
Conectar = False
Else
Conectar = True
End If
End Function
'*****************************************************************
'*****************************************************************
'*****************************************************************
Public Function Recibir(ByVal ManejadorSocket As Long, _
StringBuffer As String) As Long
'ObtenerIPDelNombre por ACHERNAR 7/2007
'para el foro de www.elhacker.net
'
'Esta funcion coloca los datos que arriban en un buffer
'Si no hay errores retorna un valor mayor que cero
'que es la cantidad de bytes recibidos
Dim ByteBuffer(1 To 8192) As Byte
Dim BytesRecibidos As Long
BytesRecibidos = recv(ManejadorSocket, ByteBuffer(1), 8192, 0&)
If BytesRecibidos > 0 Then
StringBuffer = StrConv(ByteBuffer, vbUnicode)
StringBuffer = Left$(StringBuffer, BytesRecibidos)
End If
Recibir = BytesRecibidos
End Function
Public Function Enviar(ByVal ManejadorSocket As Long, _
StringBuffer As String) As Long
'ObtenerIPDelNombre por ACHERNAR 7/2007
'para el foro de www.elhacker.net
'
'Esta funcion envia los datos colocados en el buffer
'Si no hay errores retorna un valor mayor que cero
'que es la cantidad de bytes enviados
Dim ByteBuffer() As Byte
Dim BytesAEnviar As Long
Dim BytesEnviados As Long
BytesAEnviar = Len(StringBuffer)
If BytesAEnviar > 0 Then
ByteBuffer = StrConv(StringBuffer, vbFromUnicode)
BytesEnviados = send(ManejadorSocket, ByteBuffer(0), BytesAEnviar, 0&)
Enviar = BytesEnviados
Else
Enviar = 0
End If
End Function
Public Function EstadoSocket(ByVal Manejador As Long) As Integer
'EstadoSocket por ACHERNAR 7/2007
'para el foro de www.elhacker.net
'
'esta funcion muestra el estado de un socket
'retorna valores entre 0 y 7
' 0 - Desconectado
' 1 - Lectura(se puede leer)
' 2 - Escritura(se puede escribir)
' 3 - Lectura y Escritura
' 4 - Error
' 5 - Error y Lectura
' 6 - Error y Escritura
' 7 - Error, Lectura y Escritura
Dim Retorno As Long
Dim Lectura As fd_set
Dim Escritura As fd_set
Dim Error As fd_set
Lectura.Contador = 1
Escritura.Contador = 1
Error.Contador = 1
Lectura.Arreglo(1) = Manejador
Escritura.Arreglo(1) = Manejador
Error.Arreglo(1) = Manejador
Retorno = vbselect(0&, Lectura, Escritura, Error, 0&)
If Retorno <= 0 Then
EstadoSocket = 0
GoTo Final
Else
'
'Si el socket se encuentra en estado de Lectura, Escritura o Error
'la funcion vbSelect retorna un valor MAYOR que cero (0)
'
If Lectura.Contador > 0 Then
EstadoSocket = EstadoSocket + 1
End If
If Escritura.Contador > 0 Then
EstadoSocket = EstadoSocket + 2
End If
If Error.Contador > 0 Then
EstadoSocket = EstadoSocket + 4
End If
End If
Final:
End Function
Public Function MensajeEntrante(Socket As Long) As Boolean
'MensajeEntrante por ACHERNAR 7/2007
'para el foro de www.elhacker.net
'
'Esta funcion indica si el socket está en estado
'De Lectura. si lo está retorna True.
'Si no hay datos para leer en el socket retorna False.
MensajeEntrante = False
Select Case EstadoSocket(Socket)
Case Is = 1
MensajeEntrante = True
Case Is = 3
MensajeEntrante = True
Case Is = 5
MensajeEntrante = True
Case Is = 7
MensajeEntrante = True
End Select
End Function
'*****************************************************************
'*****************************************************************
'*****************************************************************
'<el codigo termino justo antes de esta etiqueta>
hay un par de funciones que tienen cosas al pedo pero funcionan igual
como la de conectar y la de ver el estado del socket... le puse un par
de cosas mas para mostrarlas, pero no son necesarias para que funcione
ahora pongo un código para un formulario que use este modulo...
es una terminal multipropósito... seria como una especie de telnet
'<el codigo empieza justo despues de esta etiqueta>
Option Explicit
'Este proyecto necesita que se le agrege el modulo que esta en el tutorial
'Este codigo va en un formulario form1 con 4 textboxs (Text1, Text2, Text3, Text4)
'el text4 debe tener scrollbars horizontal y vertical y estar en modo Multiline
'ademas se necesitan 2 botones command1 y command2
'tambien un timer Timer1
'
'compilado tenemos una especie te telnet.. que pesa 28 KB y no necesita ningun ocx
'y comprimido con el upx pesa 10 kb... entra mas veces en un diskette que granos de
'arena en tu bolsillo XD
Private MiSocket As Long
Private Sub Command2_Click()
On Error GoTo SalidaCommand2
If Enviar(MiSocket, Trim$(Text3.Text) + vbCrLf) > 0 Then
Text4.Text = Text4.Text + "ENVIADO: " + Trim$(Text3.Text) + vbCrLf
Else
Text4.Text = Text4.Text + "% Error al intentar enviar el mensaje " + vbCrLf
End If
SalidaCommand2:
End Sub
Private Sub Form_Load()
Timer1.Interval = 100
Timer1.Enabled = False
Me.Width = 5280
Me.Height = 4170
Me.Caption = "Terminal Multiproposito por Achernar"
With Text1
.Height = 285
.Left = 120
.Top = 120
.Width = 3975
End With
With Text2
.Height = 285
.Left = 120
.Top = 480
.Width = 3975
End With
With Text3
.Height = 285
.Left = 120
.Top = 1320
.Width = 3975
End With
With Text4
.Height = 1815
.Left = 120
.Top = 1800
.Width = 4935
End With
With Command1
.Caption = "Conectar"
.Height = 615
.Left = 4200
.Top = 120
.Width = 855
End With
With Command2
.Caption = "Enviar"
.Enabled = False
.Height = 255
.Left = 4200
.Top = 1320
.Width = 855
End With
End Sub
Private Sub Command1_Click()
On Error GoTo SalidaCommand1
Dim EsIP As VbMsgBoxResult
Dim VerdaderoFalso As Boolean
If IniciarSesionSocket Then
Text4.Text = Text4.Text + "% Winsock iniciado" + vbCrLf
DoEvents
MiSocket = CrearSocket
If MiSocket <> 0 Then
Text4.Text = Text4.Text + "% Socket creado" + vbCrLf
DoEvents
EsIP = MsgBox("Si el host remoto fue ingresado como una ip --> Si" _
+ vbCrLf + "sino --> NO", vbYesNo, "Ingrese el tipo de host")
If EsIP = 6 Then
VerdaderoFalso = True
Else
VerdaderoFalso = False
End If
If Conectar(MiSocket, _
Trim$(Text1.Text), _
CLng(Trim$(Text2.Text)), _
VerdaderoFalso) Then
Text4.Text = Text4.Text + "% Conetado con el Host Remoto" + vbCrLf
DoEvents
Command1.Enabled = False
Command2.Enabled = True
Timer1.Enabled = True
Else
Text4.Text = Text4.Text + "% Error al conectar con el Host Remoto" + vbCrLf
DoEvents
TeminarSesionSocket
End If
Else
Text4.Text = Text4.Text + "% Error al crear Socket" + vbCrLf
DoEvents
TeminarSesionSocket
End If
Else
Text4.Text = Text4.Text + "% Error al iniciar Winsock" + vbCrLf
End If
GoTo Salida2Command1
SalidaCommand1:
Text4.Text = Text4.Text + "% Hubo algun error :/" + vbCrLf
DoEvents
TeminarSesionSocket
Salida2Command1:
End Sub
Private Sub Timer1_Timer()
Dim Buffer As String
If MensajeEntrante(MiSocket) Then
If Recibir(MiSocket, Buffer) > 0 Then
Text4.Text = Text4.Text + "RECIBIDO: " + Buffer
Else
Text4.Text = Text4.Text + "% Se ha perdido la conexión con el host" + vbCrLf
Command1.Enabled = True
Command2.Enabled = False
Timer1.Enabled = False
TeminarSesionSocket
End If
End If
End Sub
'<el codigo termino justo antes de esta etiqueta>
DIRECCIONES PARA INVESTIGAR MAS Y MAS Y.... BUENO
Esta es la api guide es un programa que contiene muchisimas apis
Con ejemplos y declaraciones para VB
http://www.soft-ware.net/hobby/programmierung/referenz/p03951.asp
acá estan las funciones winsock en el MSDN de Microsoft... si aprenden
a no perderse en ese mar de información les va a servir de mucho,
http://msdn2.microsoft.com/en-us/library/ms741394.aspx
y como siempre .... http://www.google.com.ar
bufff que largo... esta es la despedida... hagámoslo rápido que no estamos en un aeropuerto. Salu2.