📜 ⬆️ ⬇️

Network perversions in assembler v. 1.1

My first post in the Habra-world, please do not kick your feet, and I welcome the adequate criticism ...

It all started with a course project on the subject of JPNU (Low Level Programming Languages), at the end of which we should necessarily have invested all our creative and technical skills in creating a program in a great and powerful Assembler. The creative part was to choose a theme. Without hesitation, I decided to take something with client-server technologies and this is what came of it

The essence of the task was as follows: There are client and server parts, between which text messages should be sent (Ala SMS), while they should communicate through another program that acts as a gateway between them, redirecting messages to certain specified ports and monitoring all transmitted messages.
')
Inspired by Napoleonic ideas, I began to implement my plans. Scrolling through a lot of books on this topic, visiting many forums dedicated to programming at a low level, and almost desperately unexpectedly came across an article (on my favorite website, wasm.ru) which described the ws2_32 library. After this crucial event, the matter turned violent. I chose masm32, as the only weapon in the fight against the assigned task.

Customer


Client development took me most of the time, about 3 hours. Since I had a windowing application, I had to suffer a little with its creation (there is a lot of documentation on this subject). Skopipastiv section of the code responsible for the initialization of the window, sorting out it and sharpening for your project I got something like this:

include \masm32\include\masm32rt.inc
include Our_Socket.inc

option casemap :none

.code

start:

invoke GetModuleHandle,NULL
mov hInstance,eax
invoke GetCommandLine

invoke WinMain,hInstance,NULL,CommandLine,SW_SHOWDEFAULT
invoke WSACleanup
invoke ExitProcess,eax
invoke InitCommonControls

WinMain proc hInst:HINSTANCE,hPrevInst:HINSTANCE,CmdLine:LPSTR,CmdShow:DWORD
LOCAL wc :WNDCLASSEX
LOCAL msg :MSG
LOCAL hwnd :HWND

mov wc.cbSize,SIZEOF WNDCLASSEX
mov wc.style,CS_BYTEALIGNCLIENT
mov wc.lpfnWndProc,offset WndProc
mov wc.cbClsExtra,NULL
mov wc.cbWndExtra,NULL
push hInst
pop wc.hInstance
mov wc.hbrBackground,COLOR_BTNFACE+1
mov wc.lpszClassName,OFFSET ClassName
invoke LoadIcon,NULL,IDI_APPLICATION
mov wc.hIcon,eax
mov wc.hIconSm,eax
invoke LoadCursor,NULL,IDC_ARROW
mov wc.hCursor,eax
invoke RegisterClassEx,addr wc
invoke CreateWindowEx,0,ADDR ClassName,ADDR FormCaption,WS_SYSMENU or WS_MINIMIZEBOX,400,80,300,200,0,0,hInst,0

mov hwnd,eax
invoke MessageBox,hwnd,offset Hello,offset Header,MB_ICONINFORMATION
INVOKE ShowWindow,hwnd,SW_SHOWNORMAL
INVOKE UpdateWindow,hwnd
.WHILE TRUE
invoke GetMessage,ADDR msg,0,0,0
.BREAK .IF (!eax)
invoke TranslateMessage,ADDR msg
invoke DispatchMessage,ADDR msg
.ENDW
mov eax,msg.wParam
ret

WinMain endp

WndProc proc hWnd:HWND,uMsg:UINT,wParam:WPARAM,lParam:LPARAM
.IF uMsg == WM_DESTROY
invoke PostQuitMessage,NULL
.ELSEIF uMsg == WM_CREATE
invoke CreateWindowEx,WS_EX_CLIENTEDGE,ADDR EditClName,ADDR TextEdit1,WS_CHILD or ES_LEFT or ES_AUTOHSCROLL or WS_VISIBLE,8,8,121,21,hWnd,Edit1ID,hInstance,0

mov hwndEdit1,eax
invoke CreateWindowEx,WS_EX_CLIENTEDGE,ADDR EditClName,ADDR TextEdit2,WS_CHILD or ES_LEFT or ES_AUTOHSCROLL or WS_VISIBLE,8,35,185,21,hWnd,Edit2ID,hInstance,0

mov hwndEdit2,eax
invoke CreateWindowEx,WS_EX_CLIENTEDGE,ADDR EditClName,ADDR TextMemo1,WS_CHILD or ES_LEFT or ES_AUTOHSCROLL or ES_AUTOVSCROLL or ES_MULTILINE or WS_VISIBLE,8,60,185,89,hWnd,Memo1ID,hInstance,0

mov hwndMemo1,eax
invoke CreateWindowEx,0,ADDR BtnClName,ADDR TextButton1,WS_CHILD or BS_DEFPUSHBUTTON or WS_VISIBLE,130,6,75,25,hWnd,Button1ID,hInstance,0

mov hwndButton1,eax
invoke CreateWindowEx,0,ADDR BtnClName,ADDR TextButton2,WS_CHILD or BS_DEFPUSHBUTTON or WS_VISIBLE,205,6,75,25,hWnd,Button2ID,hInstance,0

mov hwndButton2,eax
invoke CreateWindowEx,0,ADDR BtnClName,ADDR TextButton3,WS_CHILD or BS_DEFPUSHBUTTON or WS_VISIBLE,205,32,75,25,hWnd,Button3ID,hInstance,0

mov hwndButton3,eax
.ELSE
invoke DefWindowProc,hWnd,uMsg,wParam,lParam
ret
.ENDIF
xor eax,eax
ret
WndProc endp
end start


Immediately I want to clarify the declaration of all the variables I rendered into a separate file called Our_Socket.inc, since by and large it is the same for the Server and for the Gateway.
Below is its content:

include \masm32\include\ws2_32.inc ; - winsock
includelib \masm32\lib\ws2_32.lib
option casemap :none

VERSION1_0 equ 0100h
VERSION1_1 equ 0101h
VERSION2_0 equ 0200h
AF_INET equ 2
SOCK_STREAM equ 1
SOCKET_ERR equ -1
HOSTENT_IP equ 10h
Port equ 9999
buffsz equ 255
WM_SOCKET equ WM_USER+100

WinMain proto :DWORD,:DWORD,:DWORD,:DWORD
Conecting_people proto :DWORD
Error proto :DWORD
Disconecting_people proto :DWORD

.const
Edit1ID equ 1
Edit2ID equ 4
Button1ID equ 2
Button2ID equ 3
Button3ID equ 5
Memo1ID equ 6
.data?
hInstance HINSTANCE ?
CommandLine LPSTR ?
hwndEdit1 HWND ?
hwndEdit2 HWND ?
hwndButton1 HWND ?
hwndButton2 HWND ?
hwndButton3 HWND ?
hwndMemo1 HWND ?
buff dd ?
;_______________
.data
wsadata WSADATA <0>
saServer sockaddr_in <0>
sockaddrsz dd sizeof sockaddr_in
tmp dd 0
FgSend dd ?
;buff db buffsz dup (0)
;_______________
text db 255 dup (0)
CompName dd 100 dup (0)
skt dd 0
Hello db " . © Gotech",0
Header db "SmallMessageSender 1.0 Alpha",0
Err_mess1 db " !",0
Err_mess2 db " !",0
Err_mess3 db " !",0
Err_mess4 db " !",0
Err_mess5 db "!",0
Err_mess6 db " !",0
Err_mess7 db "Listen !",0
Err_mess8 db " !",0
Suc_mess1 db "! !",0
Suc_mess2 db "Accepted!",0
Suc_mess3 db "Sucess2",0
Suc_mess4 db "!",0
Suc_mess5 db "Sucess4",0
Suc_mess6 db "Sucess5",0
Suc_mess7 db "Sucess6",0
Suc_mess8 db "Sucess7",0
;_______________
ClassName db "Mega_client",0
BtnClName db "button",0
EditClName db "edit",0
StatClName db "static",0
LboxClName db "listbox",0
CboxClName db "combobox",0
ReditClName db "richedit",0
RichEditLib db "riched32.dll",0
FormCaption db "SmallMessageClient 1.0 Alpha",0
;_______________
TextEdit1 db "death-star",0
TextEdit2 db "I agree with you!!!",0
TextButton1 db "Connect",0
TextButton2 db "Disconect",0
TextButton3 db "Send",0
TextMemo1 db 0


Further, all as in the adult program, we write the procedure for creating a socket:

Conecting_people proc hWnd:HWND
invoke WSAStartup,010001h,offset wsadata
invoke gethostbyname,addr CompName
.if eax == 0
mov edx,offset Err_mess1
invoke Error,hWnd
ret
.endif

assume eax:ptr hostent
mov ebx,[eax].h_list
mov ebx,[ebx]
mov ebx,[ebx]
mov saServer.sin_addr,ebx
assume eax:nothing

mov saServer.sin_family,AF_INET
invoke htons,Port
mov saServer.sin_port,ax
invoke socket, AF_INET,\
SOCK_STREAM,\
0
.if eax == INVALID_SOCKET
mov edx,offset Err_mess2
invoke Error,hWnd
ret
.endif
mov skt,eax

invoke WSAAsyncSelect, skt, hWnd, WM_SOCKET, FD_ACCEPT or FD_READ or FD_WRITE or FD_CLOSE or FD_CONNECT
.if eax == SOCKET_ERROR
mov edx,offset Err_mess5
invoke Error,hWnd
ret
.endif

invoke connect,skt,\
offset saServer,\
sockaddrsz
.if eax == SOCKET_ERROR
.if ecx == 2733h
.else
mov edx,offset Err_mess3
invoke Error,hWnd
.endif
ret
.endif
ret
Conecting_people endp


For a better understanding of the processes that are happening there (in fact, in order not to run Ollydbg once again) I also shoved the error handler in more or less critical sections. Well, the procedure for displaying them on the screen is attached:

Error proc hWnd:HWND
invoke MessageBox,hWnd,edx,0,MB_ICONINFORMATION
ret
Error endp


The principle of the procedure Conecting_people is no different from a similar procedure in other programming languages. Just like everywhere, the sockaddr_in structure is filled in, a socket is created, and the channel itself is created between the two machines. Perhaps the only difference here is the use of the WSAAsyncSelect procedure (described in the ws2_32 libraries) which indicates from which socket and which events should be processed.

I forgot to mention that all events are processed in the WndProc procedure, be it moving the cursor, minimizing the window, changing its size, in general, all the actions that are performed on the window, including all actions with our created socket.

In order for this procedure to work, it should be called, and we will call it by pressing the Connect button located on our window. Insert the following code into the WndProc procedure:

.ELSEIF uMsg == WM_COMMAND
mov eax,wParam
.IF lParam != 0
.IF ax == Button1ID
shr eax,16
.IF ax == BN_CLICKED
invoke GetWindowTextLength,hwndEdit1
mov tmp,eax
inc tmp
invoke SendMessage,hwndEdit1,WM_GETTEXT,tmp,addr CompName
invoke Conecting_people,hWnd
.ENDIF


Now, by pressing the Connect button, we will read the text from Edit1 and place it into the variable CompName, after which our procedure will be called.

We do the same with the procedure responsible for killing the created socket:

Disconecting_people proc hWnd:HWND
invoke closesocket,skt
.if eax != 0
mov edx,offset Err_mess4
invoke Error,hWnd
ret
.endif
ret
Disconecting_people endp


Here, in my comments are superfluous.
And we also process the pressing of the Disconect button by calling the Disconecting_people procedure.

.ELSEIF ax == Button2ID
shr eax,16
.IF ax == BN_CLICKED
invoke Disconecting_people,hWnd
.ENDIF


Now we need to send messages like that. We will do this as you probably already guessed by pressing the Send key. I did not take this whole thing into a separate procedure, but just as it is written in WndProc

.ELSEIF ax == Button3ID
shr eax,16
.IF ax == BN_CLICKED
.if FgSend!=FALSE
invoke GetWindowTextLength,hwndEdit2
mov tmp,eax
inc tmp
inc tmp
invoke SendMessage,hwndEdit2,WM_GETTEXT,tmp,addr buff
invoke send, skt, addr buff, tmp, 0
.if eax == SOCKET_ERROR
mov edx,offset Err_mess8
invoke Error,hWnd
ret
.endif
invoke SendMessage,hwndEdit2,WM_SETTEXT,NULL,0
.endif
.ENDIF
.ENDIF


The meaning here is this. If we are allowed to write to the socket (the FgSend variable will be responsible for this), then we read the size of the text residing in Edit2 (to know how much we need to send bytes), and put all its contents into the buff variable. The next thing we send all this ugliness to our socket and when errors occur, display them on the screen. In my opinion everything is quite simple.

Oh yeah, I almost forgot. It should also write processing for the socket. I spread the code, I will explain below:

.ELSEIF uMsg == WM_SOCKET
mov eax, lParam
mov edx, eax
shr edx, 16
.IF ax == FD_ACCEPT
invoke MessageBox,hWnd,offset Suc_mess2,0,MB_ICONINFORMATION
.ELSEIF ax == FD_READ
xor esi,esi

.while esi!=buffsz
mov [buff+esi],0
inc esi
.endw

invoke recv, wParam, addr buff, buffsz, 0

.if eax == SOCKET_ERROR
mov edx,offset Err_mess8
invoke Error,hWnd
ret
.endif
invoke SendMessage,hwndMemo1,WM_SETTEXT,NULL,addr buff
.ELSEIF ax == FD_CLOSE
invoke MessageBox,hWnd,offset Suc_mess4,0,MB_ICONINFORMATION
.ELSEIF ax == FD_WRITE
mov FgSend, TRUE
.ELSEIF ax == FD_CONNECT
invoke MessageBox,hWnd,offset Suc_mess1,0,MB_ICONINFORMATION
.ENDIF


Here, in fact, everything is also simple. If we have a socket set for reading, we push the entire stream of information into the variable buff, initially vanishing it. We check for errors, and if everything is normal, then we get everything we get into Memo, erasing its previous contents.
If our socket is closed, we display a message, similarly with the Connect and Accept events. Well, if we have a socket set to write, then we simply set the flag to signal what to write to the socket!

image

Server



Fully all the same difference only in one procedure, namely Connecting_people. All the difference is the replacement of the call to the connect procedure with the bind and listen procedures:

invoke bind, skt,offset saServer, sockaddrsz
.if eax == SOCKET_ERROR
mov edx,offset Err_mess6
invoke Error,hWnd
ret
.endif

invoke listen, skt, 10
.if eax == SOCKET_ERROR
mov edx,offset Err_mess7
invoke Error,hWnd
ret
.endif


image

Gateway



I will only say about the gateway that it combines the client and server parts in itself, each time receiving messages from one socket forwards them to another.

image

Source: https://habr.com/ru/post/73453/


All Articles