Sockets em Assembly
Assembly, powerful:)

Publicado em: 15/12/2016 00:00:00, por IVAN VARGAS

Programação de sockets é fundamental para desenvolver sistemas que conversam pela rede via pacotes TCP ou UDP - tais como servidores, daemons e aplicativos cliente/servidor.

Podemos trabalhar com sockets em Linguagem C, Java, PHP, Python, Ruby, etc. Mas em Assembly, será que é possível? Com certeza sim! Claro que não é tão fácil como em Python, ou elegantemente complexo como em C, mas é possível - e é isso que veremos aqui.

Este exemplo foi escrito para Windows, utilizando a ferramenta WinASM 32. Veremos um exemplo utilizando macros do WinASM - que facilitam e muito o desenvolvimento - e depois um exemplo com Assembly puro, sem utilizar as facilidades da Microsoft. Vamos lá? Siga o coelho branco... ops, digo, o código:

Exemplo Utilizando macros WinASM:

;SOCKET SERVER EM ASSEMBLY
;===========================
;Exemplo de servidor escrito em Assembly.
;Neste exemplo o server escuta na porta 2016 e replica a mensagem enviada pelo client.
;Seria uma especie de servidor de echo :)
;
;Esta versao se utiliza de macros do WinASM. Para ver como ficaria tudo isso
;o mais proximo possivel do Assembly veja o exemplo posterior :)
;
;Atencao: nao conecte-se a essa porta por telnet, ele simula enter a cada tecla digitada.
;O melhor eh utilizar o netcat:
;c:\>nc -vv localhost 2016
;
;Ivan S. Vargas | www.is5.com.br | contato@is5.com.br
;01/2016
.386
.MODEL FLAT,STDCALL

include windows.inc
include kernel32.inc
include user32.inc
include wininet.inc
include wsock32.inc

includelib kernel32.lib
includelib user32.lib
includelib wininet.lib
includelib wsock32.lib

.DATA
Caption db "Atencao!",0
SocketOk db "Socket criado com sucesso!",0
SocketErro db "Erro ao criar socket.",0
BindOk db "Porta aberta com sucesso!",0
BindErro db "Erro ao abrir porta",0
ListenOk db "Escutando na porta",0
ListenErro db "Erro em listen",0
AcceptOk db "Aguardando conexao",0
AcceptErro db "Erro em accept",0
HELO db "Ola mundo!",0
Aguardando db "Aguardando conexao",0
Version dw 2
BUFFER byte 1024 dup(?)

.DATA?
hSocket SOCKET ?
hClient SOCKET ?
wStart dw ?
socketaddr sockaddr_in <>

.CODE
	START:
	 	;carrega WSAStartup
		invoke WSAStartup,	addr Version,addr wStart
		
		;inicializa socket
		invoke socket,AF_INET,SOCK_STREAM,IPPROTO_IP
		mov hSocket, eax
		cmp hSocket, INVALID_SOCKET
		jz @erroSocket
		invoke MessageBox,NULL, addr SocketOk, addr Caption,MB_OK
		
		;preenche estrutura sockaddr
		mov socketaddr.sin_family,AF_INET
		invoke htons,2016
		mov socketaddr.sin_port,ax
		mov socketaddr.sin_addr,INADDR_ANY
	
	@bind:
		;binda a porta
		invoke bind,hSocket,addr socketaddr,SIZE socketaddr
		cmp eax,0
		jz @sucessoBind
		jmp @erroBind		
		
	@listen:
	 	;escuta na porta 
		invoke listen,hSocket,5
		cmp eax,SOCKET_ERROR
		jz @erroListen
		invoke MessageBox,NULL,addr ListenOk,addr Caption,MB_OK
		
	@accept:
		;aguarda conexoes
		;invoke accept,hSocket,addr socketaddr, SIZE socketaddr
		invoke accept,hSocket,0,0
		cmp eax,INVALID_SOCKET               ;verifica se deu erro (nao conectou)
		jz @accept                           ;se nao conectou,retorna e aguarda novamente
		mov hClient,eax                      ;se conectou,copia o endereco do socket cliente
		invoke send,hClient,addr HELO,10,0   ;envia mensagem de saudacao
		.REPEAT		
		    ;enquanto nao der erro (desconexao) recebe mensagem e a retorno (echo)
			invoke recv,hClient,addr BUFFER,1024,0
			invoke send,hClient,addr BUFFER, SIZE BUFFER,0
		.UNTIL eax == SOCKET_ERROR
		jmp @accept ;se der erro, aguarda nova conexao
		
	@erroSocket:
		invoke MessageBox,NULL,addr SocketErro,addr Caption,MB_OK
		jmp @sair
	 
	@erroBind:
	 	invoke MessageBox,NULL,addr BindErro,addr Caption,MB_OK
	 	jmp @sair
		
	@erroListen:
		invoke MessageBox,NULL,addr ListenErro,addr Caption,MB_OK
		jmp @sair
		
	@erroAccept:
		invoke MessageBox,NULL,addr AcceptErro,addr Caption,MB_OK
		jmp @sair
	
	@sucessoBind:
		invoke MessageBox,NULL,addr BindOk,addr Caption,MB_OK
		jmp @listen
	
	@sair:
		invoke ExitProcess,NULL
		
	@sucessoAccept:
	 	invoke MessageBox,NULL,addr AcceptOk,addr Caption,MB_OK
	
	END START

 

Muito bem, este foi o exemplo utilizando as funções do WinASM. Veremos agora um outro exemplo que tem a mesma lógica e propósito do código anterior, mas dessa vez iremos chamar as interrupções diretamente, passando antes os parâmetros esperados em ordem reversa para os registradores do processador - como manda o Assembly :)

 

;===========================
;SOCKET SERVER EM ASSEMBLY
;===========================
;Exemplo de servidor escrito em Assembly.
;Neste exemplo o server escuta na porta 2016 e replica a mensagem enviada pelo client.
;Seria uma especie de servidor de echo :)
;
;Para fins de comparacao, esta versao utiliza a linguagem basica do Assembly, sem
; valers-se dos pseudo codigos da Microsoft.
;
;Atencao: nao conecte-se a essa porta por telnet, ele simula enter a cada tecla digitada.
;O melhor eh utilizar o netcat:
;c:\>nc -vv localhost 2016
;
;Ivan S. Vargas | www.is5.com.br | contato@is5.com.br
;01/2016
.386
.MODEL FLAT,STDCALL

include windows.inc
include kernel32.inc
include user32.inc
include wininet.inc
include wsock32.inc

includelib kernel32.lib
includelib user32.lib
includelib wininet.lib
includelib wsock32.lib

.DATA
Caption db "Atencao!",0
SocketOk db "Socket criado com sucesso!",0
SocketErro db "Erro ao criar socket.",0
BindOk db "Porta aberta com sucesso!",0
BindErro db "Erro ao abrir porta",0
ListenOk db "Escutando na porta",0
ListenErro db "Erro em listen",0
AcceptOk db "Aguardando conexao",0
AcceptErro db "Erro em accept",0
HELO db "Ola mundo!",0
Aguardando db "Aguardando conexao",0
Version dw 2
BUFFER byte 1024 dup(?)

.DATA?
hSocket SOCKET ?
hClient SOCKET ?
wStart dw ?
socketaddr sockaddr_in <>

.CODE
	START:
	 	;carrega WSAStartup
		;invoke WSAStartup,	addr Version,addr wStart
		push offset wStart
		push offset Version
		call WSAStartup
		
		;inicializa socket
		;invoke socket,AF_INET,SOCK_STREAM,IPPROTO_IP
		push 0
		push 1
		push 2
		call socket
		mov hSocket, EAX
		cmp hSocket, -1
		je @erroSocket
		;invoke MessageBox,NULL, addr SocketOk, addr Caption,MB_OK
		push 0
		push offset Caption
		push offset SocketOk
		push 0
		call MessageBox
		
		;preenche estrutura sockaddr
		;mov socketaddr.sin_family,AF_INET
		;invoke htons,2016
		;mov socketaddr.sin_port,ax
		;mov socketaddr.sin_addr,INADDR_ANY
		mov socketaddr.sin_family,2
		push 7E0h
		call htons
		mov socketaddr.sin_port,ax
		mov socketaddr.sin_addr,0
			
	@bind:
		;binda a porta
		;invoke bind,hSocket,addr socketaddr,SIZE socketaddr
		push 10h
		push offset socketaddr
        push hSocket
		call bind
		cmp eax,0
		je @sucessoBind
		jmp @erroBind		
		
	@listen:
	 	;escuta na porta 
		;invoke listen,hSocket,5
		push 5
		push hSocket
		call listen
		cmp eax,-1
		jz @erroListen
		;invoke MessageBox,NULL,addr ListenOk,addr Caption,MB_OK
		push 0
		push offset Caption
		push offset ListenOk
		push NULL
		call MessageBox
		
	@accept:
		;aguarda conexoes
		;invoke accept,hSocket,addr socketaddr, SIZE socketaddr
		;invoke accept,hSocket,0,0
		push 0
		push 0
		push hSocket
		call accept
		cmp eax,-1                            ;verifica se deu erro (nao conectou)
		jz @accept                            ;se nao conectou,retorna e aguarda novamente
		mov hClient,eax                       ;se conectou,copia o endereco do socket cliente
		;invoke send,hClient,addr HELO,10,0   ;envia mensagem de saudacao
		push 0
		push 0Ah
		push offset HELO
		push hClient
		call send
		@repeat:
		;.REPEAT	
		    ;enquanto nao der erro (desconexao) recebe mensagem e a retorna (echo)
			;invoke recv,hClient,addr BUFFER,1024,0
			push 0
			push 400
			push offset BUFFER
			push hClient
			call recv
			;invoke send,hClient,addr BUFFER, SIZE BUFFER,0
			push 0
			push 400
			push offset BUFFER
			push hClient
			call send
		;.UNTIL eax == SOCKET_ERROR
		cmp eax,-1
		jnz @repeat
		jmp @accept ;se der erro, aguarda nova conexao
		
	@erroSocket:
		;invoke MessageBox,NULL,addr SocketErro,addr Caption,MB_OK
		push 0
		push offset Caption
		push offset SocketErro
		push NULL
		call MessageBox
		jmp @sair
	 
	@erroBind:
	 	;invoke MessageBox,NULL,addr BindErro,addr Caption,MB_OK
	 	push 0
	 	push offset Caption
	 	push offset BindErro
	 	push NULL
	 	call MessageBox
	 	jmp @sair
		
	@erroListen:
		;invoke MessageBox,NULL,addr ListenErro,addr Caption,MB_OK
		push 0
		push offset Caption
		push offset ListenErro
		push NULL
		call MessageBox
		jmp @sair
		
	@erroAccept:
		;invoke MessageBox,NULL,addr AcceptErro,addr Caption,MB_OK
		push 0
		push offset Caption
		push offset AcceptErro
		push NULL
		call MessageBox
		jmp @sair
	
	@sucessoBind:
		;invoke MessageBox,NULL,addr BindOk,addr Caption,MB_OK
		push 0
		push offset Caption
		push offset BindOk
		push NULL
		call MessageBox
		jmp @listen
	
	@sair:
		;invoke ExitProcess,NULL
		PUSH NULL
		call ExitProcess
		
	@sucessoAccept:
	 	;invoke MessageBox,NULL,addr AcceptOk,addr Caption,MB_OK
	 	push 0
		push offset Caption
		push offset AcceptOk
		push NULL
		call MessageBox
		
	END START
	
	msg MACRO texto
		push 0
		push offset Caption
		push texto
		push NULL
		call MessageBox
	EndM 

E isso é tudo. Este segundo código é mais complexo mas é o mais próximo do puro Assembly que podemos chegar com o WinASM, sem valer-se muito de suas macros e funções auxiliares.

Espero que este exemplo possa ser útil de alguma forma. É um exemplo didático, apenas para fins de estudo. Se você precisa desenvolver aplicações de rede aconselho a utilizar linguagens de nível médio (Linguagem C) ou de alto nível (Java, Python).

 

Abraço.

Ivan S. Vargas



IS5 TECNOLOGIA

São Lourenço do Sul/RS
Telefone: (53)9963-5721
E-mail: contato@is5.com.br
Facebook: fb.com/is5tecnologia
Skype: ivan_is5
GitHub: github.com/isvargas
O QUE FAZEMOS

Automação Comercial
Desenvolvimento de Sistemas
Hospedagem e Desenvolvimento de Sites
Aplicativos Mobile
Consultoria e Projetos Freelancer
Desenvolvido por is5 tecnologia