Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- ; Telnet written in 32-bit x86 assembly, using Linux system calls only
- ;
- ; Assemble and link with
- ; nasm -f elf telnet.asm -o telnet.o
- ; ld -s telnet.o -o telnet
- ;
- ; Tested on i686 Linux, kernel version 2.6.27
- ;
- ; Usage: ./telnet <IP address> <port>
- ; Note: host name resolution is not implemeneted. This version only takes IP
- ; addresses.
- ;
- ; Star Wars Telnet Demo
- ; towel.blinkenlights.nl -> 94.142.241.111
- ; Run:
- ; $ ./telnet 94.142.241.111 23
- ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
- ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
- ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
- section .data
- msgInvalidArguments: db 'Invalid IP address or port supplied!',10,0
- msgInvalidArgumentsLen: equ $-msgInvalidArguments
- msgErrorSocket: db 'Error creating socket!',10,0
- msgErrorSocketLen: equ $-msgErrorSocket
- msgErrorConnect: db 'Error connecting to server!',10,0
- msgErrorConnectLen: equ $-msgErrorConnect
- msgErrorSelect: db 'Error with select()!',10,0
- msgErrorSelectLen: equ $-msgErrorSelect
- msgUsage: db 'Usage: ./telnet <IP address> <port>',10,0
- msgUsageLen: equ $-msgUsage
- ; Arguments for socket(): socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
- socketArgs: dd 2,1,6
- ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
- ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
- ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
- section .bss
- ; Socket file descriptor returned by socket()
- sockfd: resd 1
- ; Storage for the 4 IP octets
- ipOctets resb 4
- ; Storage for the connection port represented in one 16-bit word
- ipPort resw 1
- ; Arguments for connect():
- ; connect(sockfd, serverSockaddr, serversockaddrLen);
- connectArgs resd 3
- ; The read file descriptor array for select()
- masterReadFdArray resb 128
- checkReadFdArray resb 128
- readFdArrayLen equ 128
- ; sockaddr_in structure that needs to be filled in for the
- ; connect() system call.
- ; struct sockaddr_in {
- ; short sin_family;
- ; unsigned short sin_port;
- ; struct in_addr sin_addr;
- ; char sin_zero[8];
- ; };
- serverSockaddr resb (2+2+4+8)
- serverSockaddrLen equ 16
- ; Read buffer for reading from stdin and the socket
- readBuffer resb 1024
- readBufferLen resd 1
- readBufferMaxLen equ 1024
- ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
- ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
- ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
- section .text
- global _start
- _start:
- ; Pop argc
- pop eax
- ; Check if we have the correct number of arguments (2), for the
- ; program name and IP address.
- cmp eax, 3
- je parse_program_arguments
- ; Otherwise, print the usage and quit.
- push msgUsage
- push msgUsageLen
- call cWriteString
- add esp, 8
- call cExit
- parse_program_arguments:
- ; Set the direction flag to increment, so edi/esi are INCREMENTED
- ; with their respective load/store instructions.
- cld
- ; Pop the program name string
- pop eax
- ;;; Convert the port and IP address strings to numbers ;;;
- ; Next on the stack is the IP address
- ; Convert the IP address string to four byte sized octets.
- call cStrIP_to_Octets
- add esp, 4
- ; Check for errors
- cmp eax, 0
- jl invalid_program_arguments
- ; Next on the stack is the port
- ; Convert the port string to a 16-bit word.
- call cStrtoul
- add esp, 4
- mov [ipPort], eax
- ; Check for errors
- cmp eax, 0
- jge network_open_socket
- ; Otherwise, print error for invalid arguments and quit.
- invalid_program_arguments:
- push msgInvalidArguments
- push msgInvalidArgumentsLen
- call cWriteString
- add esp, 8
- call cExit
- network_open_socket:
- ;;; Open a socket and store it in sockfd ;;;
- ; Syscall socketcall(1, ...); for socket();
- mov eax, 102
- mov ebx, 1
- mov ecx, socketArgs
- int 0x80
- ; Copy our socket file descriptor to our variable sockfd
- mov [sockfd], eax
- ; Check if socket() returned a valid socket file descriptor
- cmp eax, 0
- jge network_connect
- ; Otherwise, print error creating socket and quit.
- push msgErrorSocket
- push msgErrorSocketLen
- call cWriteString
- add esp, 8
- call cExit
- network_connect:
- ;;; Setup the argument to connect() and call connect() ;;;
- ; Fill in the sockaddr_in structure with the
- ; network family, port, and IP address information,
- ; along with the zeros in the zero field.
- mov edi, serverSockaddr
- ; Store the network family, AF_INET = 2
- mov al, 2
- stosb
- mov al, 0
- stosb
- ; Store the port, in network byte order (big endian).
- ; High byte first
- mov ax, [ipPort]
- ; Truncate the lower byte
- shr ax, 8
- stosb
- ; Low byte second
- mov ax, [ipPort]
- stosb
- ; Store the 4 octets of the IP address, reading from the
- ; ipOctets 4-byte array and copying to the respective
- ; locations in the serverSockaddr structure.
- mov esi, ipOctets
- ; movsb * 4 = movsd
- movsd
- ; Zero out the remaining 8 bytes of the structure
- mov al, 0
- mov ecx, 8
- rep stosb
- ; Setup the array that will hold the arguments for connect
- ; we are passing through the socketcall() system call.
- mov edi, connectArgs
- ; sockfd
- mov eax, [sockfd]
- stosd
- ; Pointer to serverSockaddr structure
- mov eax, serverSockaddr
- stosd
- ; serverSockaddrlen
- mov eax, serverSockaddrLen
- stosd
- ; Syscall socketcall(3, ...); for connect();
- mov eax, 102
- mov ebx, 3
- mov ecx, connectArgs
- int 0x80
- ; Check if connect() returned a success
- cmp eax, 0
- jge network_setup_file_descriptors
- ; Otherwise, print error creating socket and quit.
- push msgErrorConnect
- push msgErrorConnectLen
- call cWriteString
- add esp, 8
- jmp network_premature_exit
- network_setup_file_descriptors:
- ;;; Clear the read fd array, add stdin and the socket fd to the
- ;;; array. ;;;
- ; Point edi to the beginning of the read file descriptor array
- mov edi, masterReadFdArray
- ; Zero out all 128 bytes of the read file descriptor array
- mov al, 0
- mov ecx, readFdArrayLen
- rep stosb
- ; Add stdin, file descriptor 0, to the read file descriptor array
- mov edi, masterReadFdArray
- mov al, 1
- stosb
- ; Reset edi to the beginning of the read file descriptor array
- mov edi, masterReadFdArray
- ; Copy the value of the socket file descriptor to eax
- mov eax, [sockfd]
- ; Divide eax by 8, so we can find the offset from the beginning of
- ; the file descriptor array, so we can set the necessary bit for
- ; the socket file descriptor in the read file descriptor array.
- shr eax, 3
- ; Increment the pointer by the offset
- add edi, eax
- ; Make another copy of the socket file descriptor in ecx
- mov ecx, [sockfd]
- ; Isolate the bit offset
- and cl, 0x7
- ; Left shift a 1 to make a bit mask at that bit offset
- mov al, 1
- shl al, cl
- ; Bitwise OR the bit high at correct bit position in the array
- or [edi], al
- network_read_write_loop:
- ; Copy over the master read file descriptor array to the
- ; checking read file descriptor array, which we will pass
- ; to select and check which file descriptors are set/unset.
- mov edi, checkReadFdArray
- mov esi, masterReadFdArray
- mov ecx, readFdArrayLen
- rep movsb
- ; Syscall select(sockfd+1, readFdArray, 0, 0, 0);
- ; nfds, the first argument of select, is the highest
- ; file descriptor + 1, in our case it would be sockfd+1,
- ; since stdin is always file descriptor 0.
- mov eax, 142
- mov ebx, [sockfd]
- inc ebx
- mov ecx, checkReadFdArray
- mov edx, 0
- mov esi, 0
- mov edi, 0
- int 0x80
- ; Check the return value of select for errors
- cmp eax, 0
- jg check_read_file_descriptors
- ; Otherwise, print error calling select and quit
- push msgErrorSelect
- push msgErrorSelectLen
- call cWriteString
- add esp, 8
- jmp network_premature_exit
- check_read_file_descriptors:
- check_stdin_file_descriptor:
- ;;; Check if the stdin file descriptor is set ;;;
- ; Read the first byte (where the first bit, stdin, will be
- ; located) of the updated file descriptor array
- mov esi, checkReadFdArray
- lodsb
- ; Mask the first bit in the array
- and al, 0x01
- ; Check if it is set
- cmp al, 0x01
- jne check_socket_file_descriptor
- ; Otherwise, it is set, and we need to read the data into a
- ; buffer, and then write it to the socket
- call cReadStdin
- call cWriteSocket
- ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
- check_socket_file_descriptor:
- ;;; Check if the socket file descriptor is set ;;;
- ; Reset esi to the beginning of the read file descriptor array
- mov esi, checkReadFdArray
- ; Copy the value of the socket file descriptor to eax
- mov edx, 0
- mov eax, [sockfd]
- ; Divide eax by 8, so we can find the offset from the beginning
- ; of the file descriptor array, so we can set the necessary bit
- ; for the socket file descriptor in the read file descriptor
- ; array.
- shr eax, 3
- ; Increment the pointer by the offset
- add esi, eax
- ; Make another copy of the socket file descriptor in ecx
- mov ecx, [sockfd]
- ; Isolate the bit offset
- and cl, 0x7
- ; Left shift a 1 to make a bit mask at that bit offset
- mov bl, 1
- shl bl, cl
- ; Read the byte and mask the correct bit for the socket fd
- lodsb
- and al, bl
- ; Check if it is set
- cmp al, bl
- jne check_socket_file_descriptor_done
- ; Otherwise, it is set, and we need to read the data into a
- ; buffer, and then write it to stdout
- call cReadSocket
- call cWriteStdout
- ; Loop back to the select() system call to check for more data
- check_socket_file_descriptor_done:
- jmp network_read_write_loop
- network_premature_exit:
- network_close_socket:
- ; Syscall close(sockfd);
- mov eax, 6
- mov ebx, [sockfd]
- int 0x80
- call cExit
- ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
- ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
- ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
- ;
- ; cExit
- ; Exits program with the exit() syscall.
- ; arguments: none
- ; returns: nothing
- ;
- cExit:
- ; Syscall exit(0);
- mov eax, 1
- mov ebx, 0
- int 0x80
- ret
- ;
- ; cReadStdin
- ; Reads from stdin into readBuffer.
- ; Sets readBuffLen with number of bytes read.
- ; arguments: none
- ; returns: number of bytes read on success, -1 on error, in eax
- ;
- cReadStdin:
- ; Syscall read(0, readBuffer, readBufferMaxLen);
- mov eax, 3
- mov ebx, 0
- mov ecx, readBuffer
- mov edx, readBufferMaxLen
- int 0x80
- mov [readBufferLen], eax
- ret
- ;
- ; cReadSocket
- ; Reads from the socket sockfd into readBuffer.
- ; Sets readBuffLen with number of bytes read.
- ; arguments: none
- ; returns: number of bytes read on success, -1 on error, in eax
- ;
- cReadSocket:
- ; Syscall read(sockfd, readBuffer, readBufferMaxLen);
- mov eax, 3
- mov ebx, [sockfd]
- mov ecx, readBuffer
- mov edx, readBufferMaxLen
- int 0x80
- mov [readBufferLen], eax
- ret
- ;
- ; cWriteStdout:
- ; Writes readBufferLen bytes of readBuff to stdout.
- ; arguments: none
- ; returns: number of bytes written on success, -1 on error, in eax
- ;
- cWriteStdout:
- ; Syscall write(1, readBuffer, readBufferLen);
- mov eax, 4
- mov ebx, 1
- mov ecx, readBuffer
- mov edx, [readBufferLen]
- int 0x80
- ret
- ;
- ; cWriteSocket
- ; Writes readBufferLen bytes of readBuff to the socket sockfd.
- ; arguments: none
- ; returns: number of bytes written on success, -1 on error, in eax
- ;
- cWriteSocket:
- ; Syscall write(sockfd, readBuff, readBuffLen);
- mov eax, 4
- mov ebx, [sockfd]
- mov ecx, readBuffer
- mov edx, [readBufferLen]
- int 0x80
- ret
- ;
- ; cWriteString
- ; Prints message loaded on stack to stdout.
- ; arguments: message to write, message length
- ; returns: nothing
- ;
- cWriteString:
- push ebp
- mov ebp, esp
- ; Syscall write(stdout, message, message length);
- mov eax, 4
- mov ebx, 1
- ; Message poitner
- mov ecx, [ebp+12]
- ; Message length
- mov edx, [ebp+8]
- int 0x80
- mov esp, ebp
- pop ebp
- ret
- ;
- ; cStrIP_to_Octets
- ; Parses an ASCII IP address string, e.g. "127.0.0.1", and stores the
- ; numerical representation of the 4 octets in the ipOctets variable.
- ; arguments: pointer to the IP address string
- ; returns: 0 on success, -1 on failure
- ;
- cStrIP_to_Octets:
- push ebp
- mov ebp, esp
- ; Allocate space for a temporary 3 digit substring variable of the IP
- ; address, used to parse the IP address.
- sub esp, 4
- ; Point esi to the beginning of the string
- mov esi, [ebp+8]
- ; Reset our counter, we'll use this to iterate through the
- ; 3 digits of each octet.
- mov ecx, 0
- ; Reset our octet counter, this is to keep track of the 4
- ; octets we need to fill.
- mov edx, 0
- ; Point edi to the beginning of the temporary
- ; IP octet substring
- mov edi, ebp
- sub edi, 4
- string_ip_parse_loop:
- ; Read the next character from the IP string
- lodsb
- ; Increment our counter
- inc ecx
- ; If we encounter a dot, process this octet
- cmp al, '.'
- je octet_complete
- ; If we encounter a null character, process this
- ; octet.
- cmp al, 0
- je null_byte_encountered
- ; If we're already on our third digit,
- ; process this octet.
- cmp ecx, 4
- jge octet_complete
- ; Otherwise, copy the character to our
- ; temporary octet string.
- stosb
- jmp string_ip_parse_loop
- null_byte_encountered:
- ; Check to see if we are on the last octet yet
- ; (current octet would be equal to 3)
- cmp edx, 3
- ; If so, everything is working normally
- je octet_complete
- ; Otherwise, this is a malformed IP address,
- ; and we will return -1 for failure
- mov eax, -1
- jmp malformed_ip_address_exit
- octet_complete:
- ; Null terminate our temporary octet variable.
- mov al, 0
- stosb
- ; Save our position in the IP address string
- push esi
- ; Save our octet counter
- push edx
- ; Send off our temporary octet string to our cStrtoul
- ; function to turn it into a number.
- mov eax, ebp
- sub eax, 4
- push eax
- call cStrtoul
- add esp, 4
- ; Check if we had any errors converting the string,
- ; if so, go straight to exit (eax will hold error through)
- cmp eax, 0
- jl malformed_ip_address_exit
- ; Restore our octet counter
- pop edx
- ; Copy the octet data to the current IP octet
- ; in our IP octet array.
- mov edi, ipOctets
- add edi, edx
- ; cStrtoul saved the number in eax, so we should
- ; be fine writing al to [edi].
- stosb
- ; Increment our octet counter.
- inc edx
- ; Restore our position in the IP address string
- pop esi
- ; Reset the position on the temporary octet string
- mov edi, ebp
- sub edi, 4
- ; Continue to processing the next octet
- mov ecx, 0
- cmp edx, 4
- jl string_ip_parse_loop
- ; Return 0 for success
- mov eax, 0
- malformed_ip_address_exit:
- mov esp, ebp
- pop ebp
- ret
- ;
- ; cStrtoul
- ; Converts a number represented in an ASCII string to an unsigned 32-bit
- ; integer.
- ; arguments: pointer to the string
- ; returns: 32-bit integer stored in eax
- ;
- cStrtoul:
- push ebp
- mov ebp, esp
- ; Allocate space for the multiply operand
- sub esp, 4
- ; Point esi to the beginning of the string
- mov esi, [ebp+8]
- ; Make a copy of the string address in edi
- mov edi, esi
- string_length_loop:
- ; Load the next byte from the string
- lodsb
- ; Compare the byte to the null byte
- cmp al, 0
- ; Continue to loop until the null byte is reached
- jne string_length_loop
- ; Copy the address of the null byte + 1 and subtract the
- ; address of the string to have the string length in ebx
- mov ebx, esi
- sub ebx, edi
- ; Decrement by one to account for the null byte
- dec ebx
- ; Ensure that the string length > 0
- cmp ebx, 0
- jle premature_exit
- ; Use eax to hold the current character
- mov eax, 0
- ; Use ecx to hold the digit position in terms of powers of ten
- mov ecx, 0
- ; Use edx to hold the final result
- mov edx, 0
- ; Set esi back to the beginning of the string so we can traverse it
- mov esi, edi
- digits_count_loop:
- ; Read the next digit into al
- lodsb
- ; Decrement our string length counter
- dec ebx
- ; Start out at 10^0 = 1
- mov ecx, 1
- mov edi, 0
- ; Check if we need to multiply by any more powers of 10
- cmp ebx, edi
- ; If not, then ecx = 10^0 = 1, so we can skip the exponent
- ; multiplication loop.
- je exponent_loop_skip
- ; Otherwise, multiply ecx by 10 for however many powers
- ; the current digit requires
- exponent_loop:
- imul ecx, 10
- inc edi
- cmp ebx, edi
- jg exponent_loop
- exponent_loop_skip:
- ; Check if the character is 0 or greater
- cmp al, 48
- jge lower_bound_met
- ; Otherwise, set the result to 0 and exit
- mov eax, -1
- jmp premature_exit
- lower_bound_met:
- ; Check if the character is 9 or less
- cmp al, 57
- jle upper_bound_met
- ; Otherwise, set the result to 0 and exit
- mov eax, -1
- jmp premature_exit
- upper_bound_met:
- ; Subtract 48, the ASCII code for '0', from the character,
- ; leaving just the digit in al
- sub al, 48
- ; Multiply the powers of ten with the digit
- mov [ebp-4], eax
- imul ecx, [ebp-4]
- ; Add this digit value to the final result
- add edx, ecx
- ; Continue looping until we have gone through all the digits
- cmp ebx, 0
- jne digits_count_loop
- ; Move the result to eax
- mov eax, edx
- premature_exit:
- mov esp, ebp
- pop ebp
- ret
- ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
- ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
- ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
Advertisement
Add Comment
Please, Sign In to add comment