NASM - The Netwide Assembler
NASM Forum => Programming with NASM => Topic started by: Great_cat on March 11, 2016, 11:43:58 AM
-
Hello. Sorry for my English.
I have started learn assembly language recently. I would like create a messanger to send message from one computer to other computer. Command line is enough for me.
Please, suggest me how set up socket? What are there sockets? How include it in program?
p.s. I use NASM on Linux 32-bit and I would like send messages from Linux to Mac and from Mac to Linux.
-
I suggest you make use of the C libraries networking functions. A really good document on network programming is Beej's Guide to Network Programming (http://beej.us/guide/bgnet/). That should make things considerably easier. There are going to be incompatibilities between the way things are done on the two different systems, you can ease this by increasing the level of abstraction from the hardware through macros and making use of libraries written in high level languages that have been ported to your target systems.
-
Thank you for reply. I want make program in low-level language and on two platform: Linux and Mac. Is it looking like same? It have same kernel's calls and commands.
I would like that program will be in one file. Is it possible?
p.s. Sorry for my English and knowledge of programming. I learn it :)
-
It would be even easier to bypass the HLL and simply purchase a program to do this. I understand MicroSoft sells 'em.
However, I got the impression that Great Cat wanted to do this at a low level, specifically in Nasm. Perhaps this is an "English" problem, but I don't think so.
There is only one system call to deal with sockets. HLLs - and asm macros - treat it as if there were several - "socket", "connect", "bind", "listen", "accept", "send", recv", etc. etc. etc. These are subfunctions, "commands" - small integers which go in ebx. They use the same sys_socketcall, 102. Remaining arguments - they differ with the subfunction - are pointed to by ecx. Typically, they're on the stack, but don't have to be.
The "port" we ask to connect to determines whether we get an http server, or a mail server, or an nntp server, etc. This is just a 16-bit number (word) - big endian. No relation to the "port" we get with the "in" and "out" instructions!
I have only gotten as far as a simple "echo" server and client, on "port" 2002, and on the "loopback" connection - it doesn't actually talk to a second computer - just from one console to another. For "testing" purposes, it's easier if you don't have to be on two computers at once. :)
I don't really remember just what I did here, and they're not too well commented, but let me just throw 'em out here and see if you can get any help at all from them...
; echo server
; echos lines until "quit"
; "kill" unloads server
; runs on loopback, 127.0.0.1, and port 2002,
; no options on this model!
;
; nasm -f elf echosrv.asm
; ld -o echosrv echosrv.o
global _start
;-------------------
; should probably be in an .inc file
struc sockaddr_in
.sin_family resw 1
.sin_port resw 1
.sin_addr resd 1
.sin_zero resb 8
endstruc
; Convert numbers (constants!) to network byte order
%define hton(x) ((x & 0xFF000000) >> 24) | ((x & 0x00FF0000) >> 8) | ((x & 0x0000FF00) << 8) | ((x & 0x000000FF) << 24)
%define htons(x) ((x >> 8) & 0xFF) | ((x & 0xFF) << 8)
AF_INET equ 2
SOCK_STREAM equ 1
INADDR_ANY equ 0 ; /usr/include/linux/in.h
STDIN equ 0
STDOUT equ 1
LF equ 10
EINTR equ 4
__NR_exit equ 1
__NR_read equ 3
__NR_write equ 4
__NR_close equ 6
__NR_socketcall equ 102
; commands for sys_socketcall
; /usr/include/linux/in.h
SYS_SOCKET equ 1
SYS_BIND equ 2
SYS_CONNECT equ 3
SYS_LISTEN equ 4
SYS_ACCEPT equ 5
SYS_SEND equ 9
SYS_RECV equ 10
;------------------------
;_ip equ 0x7F000001 ; loopback - 127.0.0.1
_ip equ 0xC0A8012F ; loopback - 127.0.0.1
_port equ 2002
; Convert 'em to network byte order
IP equ hton(_ip)
PORT equ htons(_port)
BACKLOG equ 128 ; for "listen"
BUFLEN equ 1000
section .data
my_sa istruc sockaddr_in
at sockaddr_in.sin_family, dw AF_INET
at sockaddr_in.sin_port, dw PORT
at sockaddr_in.sin_addr, dd INADDR_ANY
at sockaddr_in.sin_zero, dd 0, 0
iend
socket_args dd AF_INET, SOCK_STREAM, 0
; first of these wants to be socket descriptor
; we fill it in later...
bind_args dd 0, my_sa, sockaddr_in_size
listen_args dd 0, BACKLOG
accept_args dd 0, 0, 0
section .bss
my_buf resb BUFLEN
fd_socket resd 1
fd_conn resd 1
section .text
_start:
; socket(AF_INET, SOCK_STREAM, 0)
mov ecx, socket_args ; address of args structure
mov ebx, SYS_SOCKET ; subfunction or "command"
mov eax, __NR_socketcall ;c.f. /usr/src/linux/net/socket.c
int 80h
cmp eax, -4096
ja exit
mov [fd_socket], eax
; and fill in bind_args, etc.
mov [bind_args], eax
mov [listen_args], eax
mov [accept_args], eax
mov ecx, bind_args
mov ebx, SYS_BIND ; subfunction or "command"
mov eax, __NR_socketcall
int 80h
cmp eax, -4096
ja exit
mov ecx, listen_args
mov ebx, SYS_LISTEN ; subfunction or "command"
mov eax, __NR_socketcall
int 80h
cmp eax, -4096
ja exit
again:
mov ecx, accept_args
mov ebx, SYS_ACCEPT ; subfunction or "command"
mov eax, __NR_socketcall
int 80h
cmp eax, -4096
ja exit
mov [fd_conn], eax
readagain:
; read(sock, buf, len)
push BUFLEN ; arg 3: max count
push my_buf ; arg 2: buffer
push dword [fd_conn] ; arg 1: fd
call readline
add esp, 12
test eax, eax
js closeconn
push eax ; length read is length to write
push my_buf
push dword [fd_conn]
call writeNbytes
add esp, 12
cmp dword [my_buf], 'quit'
jz closeconn
cmp dword [my_buf], 'kill'
jz killserver
jmp readagain
closeconn:
mov eax, __NR_close
mov ebx, [fd_conn]
int 80h
cmp eax, -4096
ja exit
jmp again
killserver:
mov eax, __NR_close
mov ebx, [fd_conn]
int 80h
mov eax, __NR_close
mov ebx, [fd_socket]
int 80h
goodexit:
xor eax, eax ; success
exit:
mov ebx, eax ; exitcode
neg ebx
mov eax, __NR_exit
int 80h
;-----------------------
readline:
push ebp
mov ebp, esp
sub esp, 4
pusha
%define fd ebp + 8
%define buf ebp + 12
%define max ebp + 16
%define result ebp - 4
mov dword [result], 0
mov ebx, [fd]
mov ecx, [buf]
mov edx, [max]
.reread:
mov eax, __NR_read
int 80h
cmp eax, -EINTR
jz .reread
cmp eax, -4096
ja .err
add [result], eax
cmp byte [eax + ecx - 1], LF
jz .done
add ecx, eax
sub edx, eax
jna .done
jmp .reread
.done:
popa
mov eax, [result]
mov esp, ebp
pop ebp
ret
.err:
mov [result], eax
jmp .done
%undef fd
%undef buf
%undef max
%undef result
;--------------
writeNbytes:
push ebp
mov ebp, esp
sub esp, 4
pusha
%define fd ebp + 8
%define buf ebp + 12
%define Nbytes ebp + 16
%define bytesleft ebp - 4
mov ebx, [fd]
mov ecx, [buf]
mov edx, [Nbytes]
mov [bytesleft], edx
.rewrite:
mov eax, __NR_write
int 80h
cmp eax, -EINTR
jz .rewrite
cmp eax, -4096
ja exit
sub [bytesleft], eax
jz .done
add ecx, eax
sub edx, eax
jna .done
jmp .rewrite
.done:
popa
mov eax, [Nbytes]
%undef fd
%undef buf
%undef Nbytes
%undef bytesleft
mov esp, ebp
pop ebp
ret
... and a client...
;-----------------------------
;
; nasm -f elf32 echocli.asm
; ld -o echocli echocli.o
global _start
struc sockaddr_in
.sin_family resw 1
.sin_port resw 1
.sin_addr resd 1
.sin_zero resb 8
endstruc
_ip equ 0x7F000001 ; loopback - 127.0.0.1
;_ip equ 0x48400C6B ; loopback - 127.0.0.1
_port equ 2002
; Convert numbers to network byte order
IP equ ((_ip & 0xFF000000) >> 24) | ((_ip & 0x00FF0000) >> 8) | ((_ip & 0x0000FF00) << 8) | ((_ip & 0x000000FF) << 24)
PORT equ ((_port >> 8) & 0xFF) | ((_port & 0xFF) << 8)
AF_INET equ 2
SOCK_STREAM equ 1
BUFLEN equ 0x80
STDIN equ 0
STDOUT equ 1
LF equ 10
EINTR equ 4
__NR_exit equ 1
__NR_read equ 3
__NR_write equ 4
__NR_socketcall equ 102
SYS_SOCKET equ 1
SYS_CONNECT equ 3
section .data
my_sa istruc sockaddr_in
at sockaddr_in.sin_family, dw AF_INET
at sockaddr_in.sin_port, dw PORT
at sockaddr_in.sin_addr, dd IP
at sockaddr_in.sin_zero, dd 0, 0
iend
socket_args dd AF_INET, SOCK_STREAM, 0
; first of these wants to be socket descriptor
; we fill it in later...
connect_args dd 0, my_sa, sockaddr_in_size
section .bss
my_buf resb BUFLEN
sock_desc resd 1
section .text
_start:
; socket(AF_INET, SOCK_STREAM, 0)
mov ecx, socket_args ; address of args structure
mov ebx, SYS_SOCKET ; subfunction or "command"
mov eax, __NR_socketcall ;c.f. /usr/src/linux/net/socket.c
int 80h
cmp eax, -4096
ja exit
mov [sock_desc], eax
; and fill in connect_args
mov [connect_args], eax
; connect(sock, (struct sockaddr *)&sa, sizeof(struct sockaddr))
mov ecx, connect_args
mov ebx, SYS_CONNECT ; subfunction or "command"
mov eax, __NR_socketcall
int 80h
cmp eax, -4096
ja exit
again:
push BUFLEN
push my_buf
push STDIN
call readline
add esp, 12
push eax
push my_buf
push dword [sock_desc]
call writeNbytes
add esp, 12
cmp dword [my_buf], 'quit'
jz goodexit
push BUFLEN
push my_buf
push dword [sock_desc]
call readline
add esp, 12
push eax
push my_buf
push STDOUT
call writeNbytes
add esp, 12
jmp again
goodexit:
xor eax, eax ; success
exit:
mov ebx, eax ; exitcode
neg ebx
mov eax, __NR_exit
int 80h
readline:
push ebp
mov ebp, esp
sub esp, 4
pusha
%define fd ebp + 8
%define buf ebp + 12
%define max ebp + 16
%define result ebp - 4
mov dword [result], 0
mov ebx, [fd]
mov ecx, [buf]
mov edx, [max]
.reread:
mov eax, __NR_read
int 80h
cmp eax, -EINTR
jz .reread
cmp eax, -4096
ja exit
add [result], eax
cmp byte [eax + ecx - 1], LF
jz .done
add ecx, eax
sub edx, eax
jna .done
jmp .reread
.done:
popa
mov eax, [result]
%undef fd
%undef buf
%undef max
%undef result
mov esp, ebp
pop ebp
ret
writeNbytes:
push ebp
mov ebp, esp
sub esp, 4
pusha
%define fd ebp + 8
%define buf ebp + 12
%define Nbytes ebp + 16
%define bytesleft ebp - 4
mov ebx, [fd]
mov ecx, [buf]
mov edx, [Nbytes]
mov [bytesleft], edx
.rewrite:
mov eax, __NR_write
int 80h
cmp eax, -EINTR
jz .rewrite
cmp eax, -4096
ja exit
sub [bytesleft], eax
jz .done
add ecx, eax
sub edx, eax
jna .done
jmp .rewrite
.done:
popa
mov eax, [Nbytes]
%undef fd
%undef buf
%undef Nbytes
%undef bytesleft
mov esp, ebp
pop ebp
ret
I don't know too much about this stuff, and don't remember all of the little I learned, but I'll try to answer questions if I can.
You should also read Beej's Guide, of course!
Best,
Frank
-
Hi Great cat,
Wanting this to run on two systems makes C (or some other HLL) look like a better idea. It is possible to make the same file run on Linux and BSD, and Mac is "like" BSD, so it's probably possible. How "new" are you? Can you make any program at all run on both Linux and Mac? Take a look at http://asm.sf.net - in the "asmutils" section there are macros which will allow the same source code to assemble and run on Linux and BSD. It can probably be modified to work on Mac. Probably not a good project for a beginner. (sorry to say).
Best,
Frank
-
Hello Frank Kotler.
Thank you very much for your reply.
I'm beginner in assembly. I can use kernel's calls. I can determine variables and make procedure. I bad understand syntax of the program, but I will learn it more detail. Thank you for information. I have 2 computers with Linux.
I can get access to computer with Mac in public place.
I thought that program on Linux (64-bit) with a few changes will work on Mac. Is it so?
p.s. What is HLL?
-
"HLL" = "High Level Language" Such as C - designed so the same source code will compile (differently!) and run on multiple platforms. Assembly Language is definitely not designed that way!
My understanding is that BSD will run a Linux program - with minor changes - parameters go on the stack instead of in registers and a "stub" needs to be called to do the int 80h. It does need the minor changes! This applies to 32-bit code. I don't know 64-bit. Linux uses different system call numbers for 64-bit code, I understand Mac uses the same numbers as 32-bit (but a different calling convention!).
Best,
Frank
-
Thank you very much Frank Kotler.
I will try your program and I will learn assembly and C.
-
It would be even easier to bypass the HLL and simply purchase a program to do this. I understand MicroSoft sells 'em.
However, I got the impression that Great Cat wanted to do this at a low level, specifically in Nasm. Perhaps this is an "English" problem, but I don't think so.
When I originally read his question, I started out to write a quick echo client/server by grep'ing unistd_32.h and brushing up on the manual pages for socketcall.. then I noticed the p.s. portion of his post. Unfortunately, adding the requirement of cross-platform development means the project is no longer a trivial newbie project. That is, unless, you use existing libraries that are available on both architectures and abstract subtle differences between the systems, like calling conventions, away with macros.
The funny thing is, when I posted my comment I actually thought to myself "Man, I'm suggesting HLL libraries way too much on this forum." However, there are situations in which the added abstraction is very handy and I rather prefer to suggest that as a solution rather than putting them on a path of development and exploration which would even be difficult for veteran assembler programmers.
Great_cat,
When you're not dealing with the operating system, most of your code can very easily be swapped from one system to another with very little, if any changes. These are usually in the form of procedures written to preform some specific calculations or data manipulations. A good example of this kind of problem would be random number generators or architecture extension specific math routines for optimal performance. But once you start interfacing with the operating system, things get difficult. There is no universal standard by which all operating systems handle networking, or calling procedures, or interfacing between processes. All of these, and more, problems require you to have a decent knowledge of the target system. This isn't unreasonable for a newbie, but doing this on two different operating systems and figuring out how to solve the same problem within the domain of solutions provided by the operating system isn't something I wish on any "new" programmer.
When I mentioned the C library above, there was a reason for that. The C language itself imposes certain requirements for how things should be done. This means that interfacing with the C library should be pretty uniform across the entire library. This will greatly reduce the amount of problems you're going to run into when switching between different operating systems. This doesn't mean you won't run into any problems, there are distinct differences in how C handles calling procedures on 32-bit and 64-bit systems, but this is where implementing macros to abstract away calling conventions comes into play (and is a great practice for "new" programmers to learn to use macros).
Probably the biggest down-side to working within the C library is when you run into C's single-line macros in the headers. Your first instinct will usually be to try and turn those #define lines directly into NASM %define's, but this doesn't normally work out too well. You have to remember that the NASM %define single-line macro only supports compile-time expansion and C can expand to statements which may then be compiled into instructions. Just be aware that sometimes when you see #define single-line macros, you might be better off porting it using %macro instead of %define.