Trao đổi với tôi

http://www.buidao.com

1/22/10

[TASM] MicroSoft Windows Sockets Guide

by Bumblebee/29a

Tranz by Benina

Index

~~~~~

1.Overview (tổng quan)

2.What is windows sockets? (windows sockets là gì?)

3.Micro$oft extensions (các hàm mở rộng của M$)

4.Step by step: make a connection (hường dẫn từng bước tạo một kết nối)

5.Read and Write (đọc và viết)

Apendix: wsocks.inc (thư mục đính kèm)

1.Overview

~~~~~~~~~~

Đây ko phải là một bài hướng dẫn đặc biệt gì cả . Chỉ là những ghi chép lại việc tôi tìm hiểu qua quá trịnh thực hiện win32 sockets of chuẩn BSD socket standards. Bài này có thể giúp bạn làm một remote connection (liên kết từ xa) với virus của bạn vì dụ như để send một e-mail chẳng hạn.

Nói đơn giản là mô tả một đọan code từ i-worm.Anaphylaxis.

2.What is windows sockets?

~~~~~~~~~~~~~~~~~~~~~~~~~~

Đặc điểm của Windows Sockets là định nghĩa một giao diện lập trình mạng network programming cho Microsoft Windows, mà nó được đặt nền tảng trên mô hình "socket" đã được phổ biến tại Berkeley Software Distribution (BSD). Nó bao gồm cả hai lọai “Berkeley socket style routines” (các thủ tục theo kiểu mẫu socket của Berkeley) và cài đặt các phần mở rộng đặc trưng của Windows (Windows-specific extensions) rất quen thuộc đã được thíết kế cho phép lập trình viên có được tiện lợi điều khiển thông điệp một cách tự nhiên trong Windows.

Nhưng, socket là cái quái gì vậy? Ý tưởng của BSD là một file để điều khiển từ xa máy tính (machine). Sokects đươc sử dụng trong BDS giống như các files đơn giản. Bạn có thể sử dụng thông thường như 'write' và 'read' để viết và đọc từ socket. Nhưng M$ thay đổi sự trừu tượng của level (lớp) này và cho bạn các hàm đặc biệt để làm cho bạn nhớ rằng bạn đang sử dụng các hàm API chết tiệt của họ.

3.Micro$oft extensions

~~~~~~~~~~~~~~~~~~~~~~

Da6y là các hàm functions được cung cấp bởi M$ mà chúng ko có trong

Berkeley:

         WSAAsyncGetHostByAddr()
WSAAsyncGetHostByName()
WSAAsyncGetProtoByName()
WSAAsyncGetProtoByNumber()
WSAAsyncGetServByName()
WSAAsyncGetServByPort()
WSAAsyncSelect()
WSACancelAsyncRequest()
WSACancelBlockingCall()
*WSACleanup()
WSAGetLastError()
WSAIsBlocking()
WSASetBlockingHook()
WSASetLastError()
*WSAStartup()
WSAUnhookBlockingHook()

M$ cung cấp các hàm quỷ quái này bởi vì cách hoạt động của Windows là thực hiện đa tác vụ (multitasking). Chuyện gì xảy ra nếu một ứng dụng chờ một sự kết nối(connection)? Kết nối (connection) yêu cầu ứng dụng phải “xem xét” nó một cách bắt buộc nếu kết nối là ok. Vì vậy ứng dụng ko thể lấy các thông điệp windows đang gởi đến chúng. Các hàm chỉ ra phía trên cung cấp các công việc không bị ngăn chặn (nonblocking work) như cơ chế nói trên.

Chúng ta chỉ quan tâm đến các hàm đánh dấu sao '*'. Những hàm khác ko cần thiết vì tôi sẽ sử dụng socks theo cách thức BSD (BSD=UNIX=LUNUX rulez!).

4.Step by step: make a connection

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Bước đầu tiên chúng ta làm là kiểm tra sự cài đặt wsocks trong máy tính. Chúng ta sẽ sử dụng wsocks 1.1, vì vậy chúng ta tạo ra một check (kiểm tra) với hàm WSAStartup.

        push    offset wsadata                  ; WSADATA struct
push VERSION1_1 ; we want version 1.1
call WSAStartup ; check wsocks installation
cmp eax,0 ; on error:
jne qApp ; exit app

mov ax,VERSION1_1 ; WSAStartup returns the version
cmp ax,word ptr [wsadata.mVersion] ; test version
jne exitAppQsocks ; quit sockets and exit App

Hàm WSAStartup cần 2 đối số: một con trỏ pointer đến cấu trúc WSADATA struct và số version yêu cầu cần thiết để ứng dụng hoạt động. Hàm API này trả về (retuns) trong thành phần .mVersion của cấu trúc WSADATA số version thấp nhất có thể sử dụng mà bạn cần biết hay sẽ báo lỗi xảy ra nếu hàm thất bại. Hơn nữa hàm WSAStartup sẽ báo cho Windows biết bạn đang sử dụng wsocks. Vì vậy khi version mà bạn yêu cầu ko phù hợp thì bạn cần thiết phải gọi hàm WSACleanup để xóa bỏ trạng thái của Windows bị gây ra trước đó do gọi hàm WSAStarup:

        call    WSACleanup                ; end wsocks use

Giả sử bây giờ chúng ta có version mà chúng ta cần. Tại điềm này chúng ta cần open một socket.

        push    PCL_NONE            ; protocol
push SOCK_STREAM ; type of socket
push AF_INET ; family
call socket ; open a socket
cmp eax,SOCKET_ERR ; on error:
je doCleanUp ; WSACleanup

Hàm socket cần 3 tham số: protocol, type và family.

Tham số protocol có thể được set nhưng điều này có thể làm cho cơ chế bảo mật của windows hủy bỏ kết nối (connection) của chúng ta. Ví dụ: chúng ta muốn tạo ra một telnet connection. Nếu chúng ta set protocol thành telnet protocol và Windows thì ko cho phép sử dụng protocol này thì socket của chúng ta sẽ bị sai (fails). Vì vậy cách tốt nhất là set protocol là none.

Tham số type of socket có thể là: STREAM hay DATAGRAM. Đầu tiên là định hướng đến kết nối (connection) và thứ hai là gởi các packets mà chúng có thể đến nơi nhận (receiver) theo thứ tự khác nhau khi chúng đã được gởi đi. Hơn nữa người gởi (sender) sẽ ko thể biết được packet có đến nơi nhận hay ko khi ko cần sự trợ giúp phản hồi của người nhận (receiver).

Tham số sau cùng family có thể là: AI_UNIX, AF_INET... nhưng chỉ các AF_INET addresses được hổ trợ trong version mà tôi kiểm tra.

Chúng ta sử dụng PCL_NONE, SOCK_STREAM và AF_INET. Hàm socket trả về (returns) giá trị SOCKET_ERR (-1) nếu hàm gọi bị sai và trả về socket handle nếu hàm gọi thành công.

Bước cuối cùng là tạo ra một kết nối (connection). Theo lý thuyết chúng ta connect (kết nối) socket, điều đó được dùng để quản lý kết nối (connection), là điều khiển từ xa máy tính. Trước tiên chúng ta cần lắp giá trị vào cấu trúc SOCKADDR struct (đây là cấu trúc đã bị chỉnh sửa (modified struct): tôi đã kết hợp các cấu trúc khác lại vì vậy chúng ta sẽ chỉ sử dụng cho loại AF_INET).

    SOCKADDR        struct
sin_family dw 0 ; ever AF_INET
sin_port dw 0 ; the port
sin_addr dd 0 ; addr of server node
sin_zero db 8 dup(0) ; not used
SOCKADDR ends

sin_family thì dễ dàng được lắp trị vào, nhưng sin_port và sin_addr thì phức tạp hơn. sin_port là port (cổng) mà ở đó chúng ta muốn connect(kết nối). Nhưng số này phải là số byte thứ tự của network (network byte order)(nguyên cứu thêm về mô hình mạng). Sockets cung cấp hàm htons để làm điều này:

        push    PORT                    ; number of port
call htons ; get port in network byte
mov word ptr [sockaddr.sin_port],ax ; order

Hàm htons lấy port và trả về returns một WORD chứa network byte order.

sin_addr thì phức tạp nhất. Chúng ta cần addr of host đề connect (kết nối). Đây là một số number dùng để nhận dạng ra nút (node) trên mạng (mạng là sự truyền thông giữa các node). Nhưng thông thường chúng ta lại có định dạng như sau 'domain.ext' (ví dụ: IBM.com, netscape.com,...). Vì vậy chúng ta phải lấy IP của nó (xxx.xxx.xxx....) mà chúng có định dạng dotted format và rồi lấy địa chỉ của nó.

        push    offset server            ; addr of the string ('oeee.net')
call gethostbyname ; get the hostent struct
cmp eax,0 ; on error:
je exitQsocksC ; close sock, cleanup and exit app
; eax contains the pointer to HOSTENT

mov eax,dword ptr [eax+HOSTENT_IP] ; get pointer to IP into HOSTENT
mov eax,dword ptr [eax] ; get pointer to IP
mov dword ptr [sockaddr.sin_addr],eax ; that's all!

push sizeOfSockaddr ; the size of sockaddr struct
push offset sockaddr ; the addr of sockaddr
push dword ptr [fd] ; the handle of the socket
call connect ; connect now!
cmp ax,SOCKET_ERR ; on error:
je exitQsocksC ; close sock, cleanup and exit app

Ví dụ này tương đối đầy đủ rõ ràng: chúng ta lấy trong cấu trúc HOSTENT mà nó có thành phần HOSTENT_IP là addr IP of nút (node) trên mạng. Rồi lắp giá trị vào sin_addr và cấu trúc sockaddr struct bây giờ đã sẳn sàng cho việc tạo ra kết nối (connection). Hàm connect cần các tham số sau: size of SOCKADDR struct (là hằng số vì do tôi đã chỉnh sửa cấu trúc mô tả trước đó ;), pointer đến SOCKADDR struct và handle of socket.

That's all. Chúng ta chỉ cần close socket khi công việc hoàn thành ta sử dụng hàm closesocket:

        push    dword ptr [fd]            ; handle of the socket
call closesocket

5.Read and Write

~~~~~~~~~~~~~~~~

Miscro$oft cung cấp các hàm APIs khác để đọc và viết nhưng chúng ta sẽ sử dụng hàm: send và recv.

        push    0                    ; normal (can be OOBD too)
push ecx ; size of message to send
push esi ; addr to message
push eax ; socket handle
call send

push 0 ; normal
push 4 ; size to read
push offset response ; addr to store message
push eax ; soket handle
call recv

Hàm send làm việc và cho ra các errors (lỗi) tương tự như hàm _lwrite và cũng xảy ra tương tự với hàm recv và hàm _lread tương ứng.

Hàm send và recv đang bị block (ngăn chặn). Điều này có nghĩa là nếu bạn đang sending hay receiving và không có dữ liệu nào được send/receive, thì socket sẽ ngăn chặn ứng dụng cho đến khi có data sẳn dàng để dùng, kết nối (connection) bị sai fails hay process kết thúc. Vì vậy đây là điều cuối cùng những gì chúng ta cần sử dụng. Chúng ta cài đặt một thread và thead này tạo ra một kết nối (connection) và sends/receives (gởi/nhận) các thông điệp messages (tạo ra sự truyền thông). Main process (tiến trình chính) mà chúng cài đặt ra thread chỉ để chờ time (thời gian) cho phép thread làm việc. Nhưng nếu thread bị blocked time (thời gian) trong main process mất hiệu lực . Thì main process kết thúc thread và tiếp tục công việc của nó.

That's all folks!

 APENDIX: wsocks.inc

;
; WSocks.inc: include file for windows sockets .
; Designed for TASM5 and Win32.
;
; (C) 1999 Bumblebee.
;
; This file contains basic structures and stuff to work
; with windows sockets.
;

; Descriptions of the API:
; arguments in order of PUSH ;)

; only for debug
extrn WSAGetLastError:PROC

; starts the use of winsock dll
; addr WSADATA, version requested
; returns: 0 ok
extrn WSAStartup:PROC

; terminates the use of winsock dll
; returns: SOCK_ERR on error
extrn WSACleanup:PROC

; opens a new socket
; protocol (PCL_NONE), type (SOCK_??), addr format (AF_??)
; returns: socket id or SOCKET_ERR (socket is dw)
extrn socket:PROC

; closes a socket
; socket descriptor
;
extrn closesocket:PROC

; sends data (this socks are a shit... Unix uses simple write)
; flags (1 OOB data or 0 normal ) , length, addr of buffer, socket
; returns: caracters sent or SOCKET_ERR on error
extrn send:PROC

; reveives data (this socks are a shit... Unix uses simple read)
; flags (use 0), length, addr of buffer, socket
; returns: caracters sent or SOCKET_ERR on error
extrn recv:PROC

; connects to a server
; sizeof struct SOCKADDR, struct SOCKADDR, socket
; returns: SOCKET_ERR on error
extrn connect:PROC

; gets the name of the current host
; length of the buffer for name, addr of buffer for name
; return: SOCKET_ERR on error
extrn gethostname:PROC

; gets strcut hostent
; addr of name
; returns: ponter to the struct or 0 on error
extrn gethostbyname:PROC

; converts a zstring like "xxx.xxx.xx...." to netw byte order
; zstring ptr to change to dotted addr format
; returns: in_addr (dd)
extrn inet_addr:PROC

; dw to convert into netw byte order (usually the port)
; returns: the value in network byte order (dw)
extrn htons:PROC

; Structs :o

; sockaddr struct for connection
; modified (for better use)
; if you want the original look for it into a winsock.h
SOCKADDR struct
sin_family dw 0 ; ex. AF_INET
sin_port dw 0 ; use htons for this
sin_addr dd 0 ; here goes server node (from inet_addr)
sin_zero db 8 dup(0)
SOCKADDR ends

; for WSAStartup diagnose
WSADATA struct
mVersion dw 0
mHighVersion dw 0
szDescription db 257 dup(0)
szSystemStatus db 129 dup(0)
iMaxSockets dw 0
iMaxUpdDg dw 0
lpVendorInfo dd 0
WSADATA ends

; Some nice equs

; what version of winsock do you need? (usually 1.1)
VERSION1_0 equ 0100h
VERSION1_1 equ 0101h
VERSION2_0 equ 0200h

AF_UNIX equ 1 ; local host
AF_INET equ 2 ; internet (most used)
AF_IMPLINK equ 3 ; arpanet
AF_NETBIOS equ 17 ; NetBios style addresses

; types of sockets
SOCK_STREAM equ 1 ; stream (connection oriented; telnet like)
SOCK_DGRAM equ 2 ; datagram (packets, packets, packets)

; protocol
PCL_NONE equ 0 ; none (define the protocol not needed)

SOCKET_ERR equ -1 ; standard winsock error

HOSTENT_IP equ 10h ; where is the IP into the hostent struct


APENDIX ENDS