|
|
采用成熟的MFC框架技术来搭建远控客户端和服务端,实现了进程管理、文件管理、服务管理、远程SHELL和屏幕监视功能,层次结构清晰,为日后软件版本的迭代留下了扩展空间。编程环境Visual Studio 2010连接方式采用反弹型连接方式,被控端主动连接控制端从而能够轻松穿透大多数防火墙。工作流程 基本传输结构1、被控端上报基本计算机信息结构被控端连接控制端,并将计算机信息上报控制端显示。typedef struct tagSytemInit{ char computer[32]; //计算机名 char user[32]; //用户名 char os[72]; //操作系统 char processor[16]; //处理器信息 char mem[16]; //内存信息 char version[16]; //软件版本 char HDSerial[32]; //硬盘序列号 }SYSTEMINIT,*LPSYSTEMINIT; 2、临时连接结构该结构用来存储连接到控制端上的socket信息以及相应的硬盘序列号。在后面的使用中将此结构存储到vector中用于管理被控端。typedef struct tagTmpSocket{ SOCKET ClientSocket; char HDSerial[64];}TMPSOCKET,*LPTMPSOCKET; 3、进程通信结构控制端控制被控端,实现进程之间的通信。typedef struct tagLinkInfo { SOCKET s; string strBindIp; //被控端IP u_short BindPort; //监听端口 }LINKINFO,*LPLINKINFO; 基本通信类CTcpTran是整个远控的基础通信类,用于实现socket网络通信的初始化,封装相应的API函数。使用类来封装Socket API可以避免代码的重复,便于调试。CTcpTran类中的4个基本成员函数如下:SOCKET InitSocket(int SocketType, string strBindIp,u_short BindPort,int opt); //初始化socket,选择连接类型 SOCKET myaccept(SOCKET s,struct sockaddr* addr,int* addrlen); //本地监听处理函数 int mysend(SOCKET sock, const char *buf, int len, int flag,int overtime); //发送数据 int myrecv(SOCKET sock, char *buf, int len, int flag , int overtime,char*EndMark,BOOL soonflag=FALSE); //接收数据 InitSocket函数InitSocket参数解释如下,SocketType为连接类型,当值为 SOCKET_BIND时表示绑定本地端口,服务器监听端口等待客户端来连接,当值为SOCKET_NOBIND时表示不绑定,服务端主动连接客户端。strBindIp为要绑定的IP地址,”"(空)为本地任意地址,这样做的目的是当服务器有多块网卡时,不论哪个网段上的客户程序都能与服务器通信。uBindPort为要绑定的端口。SOCKET CTcpTran::InitSocket( int SocketType, string strBindIp,u_short BindPort,int opt)
: b& D: e( `) j$ [$ @( m{
# j% F: k8 F5 D, D ` SOCKET socketid = INVALID_SOCKET;9 a' a% ]3 [ n7 W0 _6 g! x
socketid = socket(PF_INET,SOCK_STREAM,0); //建立一个流式套接字句柄
2 d2 B" U: c' z% k" j% \7 t SOCKADDR_IN sockStruct; //初始化一个地址结构. Q$ z5 a4 b. J
sockStruct.sin_family = AF_INET; //使用TCP/IP协议; C+ l* m# C1 e* k% f
+ Y e$ {( @$ w& sif( strBindIp.empty() )9 \+ G4 _6 d( _
{0 H. H2 ]% j4 L1 v# \; m" w- x
sockStruct.sin_addr.S_un.S_addr = INADDR_ANY; //如果strBindIp为空,则为本地任意地址
6 U5 ~. `& b0 ~* R3 R
/ i; Q% g' y6 q, [* B5 o }else
+ Y3 s& j$ L# e! u+ g7 z {
' \& N. h: L) w$ J: Z sockStruct.sin_addr.S_un.S_addr = inet_addr(strBindIp.c_str());
/ u9 p/ e6 t6 d6 I }
, \* l! U" f2 ]% j* z3 }$ Q; q$ J) p2 `( B3 d
: @( N. e% T5 \; X
sockStruct.sin_port = htons(BindPort); //转换为网络字节8 C3 w* ^% z) N; H. {8 b
( ?* t, O `" e3 d0 x
if( SocketType == SOCKETNOBIND )
. l9 W9 u: x* w! h' G {
- a; n+ y1 ]' U5 `7 J0 w: ^4 g# Rif(connect(socketid,(LPSOCKADDR)&sockStruct,sizeof(sockStruct)) == SOCKET_ERROR) //不绑定,直接连接,被控端选择非绑定方式连接) b% d$ @/ e* r, H8 @- K
{( p" b/ t( C- l- \
// AfxMessageBox("InitSocket 错误");+ c" @* x2 w% G7 G9 T/ N
closesocket(socketid);2 w6 j* n. l. x& N! O$ W( D
shutdown(socketid,2);/ p, }. k" p7 v2 r% ^
socketid = INVALID_SOCKET;
: G7 P1 L& j1 e k }5 r. D' C# f3 b- T% y
/ @6 O3 V. f$ ^. O7 U7 F
m_Socket = socketid;7 ?# L0 l# Q) t& z8 I+ F
J; G5 K: L9 A Q# R7 m
}else if( SocketType == SOCKETBIND ) //控制端选择绑定本地端口5 ~# c% y1 D% w, t* G" y2 e) S
{
" @3 _6 T: |! t/ J: q2 qif(bind(socketid,(sockaddr*)&sockStruct,sizeof(sockaddr_in)) == SOCKET_ERROR) //绑定地址结构
2 v! d- S, g; f3 w2 d* p1 z; @ {
" L- i4 d5 v; j& a" w+ G- d- l/ v closesocket(socketid);
+ P4 w! s! J' X8 l! ^ socketid = INVALID_SOCKET;; n! `" g" r- M* Y0 b: a
2 u. t" M9 _% a' ~9 t
}else6 E P/ ]: W E% o
{
9 h3 K# m; a5 ?if( listen(socketid,SOMAXCONN) == SOCKET_ERROR ) //进入监听) P* z- m& P* }
{
6 M' r% }( s K0 O# E closesocket(socketid);. d4 _) Q6 n. x, b" {% O
socketid = INVALID_SOCKET;1 ?9 g4 E6 x/ P8 h+ i* c0 @
}! X0 E0 D/ g: }# o
}
) R4 J4 n8 ~7 D0 l W% i# R- G0 f9 U0 |2 y6 ^5 C
m_Socket = socketid;
( j9 N7 G, b; {) I- C }; B5 Z! R3 z9 ~8 L6 M" `' ^$ Q
7 G, y5 n- s7 C _4 |( Areturn socketid; //返回建立的socket
6 X& F+ K5 E" d* p} myaccept函数服务器接收客户端的连接请求,创建一个新的套接字和参数addr指定的客户端套接字建立连接通道。s表示处于监听状态的流套接字。addr表示新创建的套接字地址结构。addrlen表示新创建套接字的地址结构的长度。SOCKET CTcpTran::myaccept(SOCKET s,struct sockaddr* addr,int* addrlen)% k K# e/ m" `7 n
{
% Z+ M- n) \2 v3 L/ |1 r" ? SOCKET accpsocket = INVALID_SOCKET;
2 ?/ [+ w, V, I9 ?. Q' _ accpsocket = accept(s,addr,addrlen);
7 D; |8 o: ~ Ureturn accpsocket;3 g: Q3 b. v* u% b; ]
}: I1 ^) e- X' q- u) J
mysend函数mysend函数用来发送指定的套接字数据。sock为指定的Socket。buf为用来存放要发送的数据的缓冲区。len为待发送数据的长度。flag一般设置为0。overtime为超时时间。这里采用了select机制防止I/O操作阻塞,提高了程序运行效率。这里要注意每次执行select操作之前都要更新文件描述符,因为select操作会更改文件描述符。int CTcpTran::mysend(SOCKET sock, const char *buf, int len, int flag,int overtime)
) v: B' n( }% l- Z) p( i{
, D2 ]7 Z/ ?7 Gint ret;/ W) H3 S, P. P
int nLeft = len; //待发送的字节数% {0 l! w1 l5 r8 L4 T+ t: M
int idx = 0; //发送缓冲区索引, {1 ]2 |3 G' s0 C/ W
2 q: H0 J$ Y) I3 e
fd_set readfds; ! ~7 S7 Q5 ` Q( o: K: \, p* ]
struct timeval timeout;
; s( b9 J6 O1 m% x0 t& i6 N4 I1 M timeout.tv_sec = 0;
7 q7 X, f _- n3 G2 Y+ E4 \ timeout.tv_usec = 500; B/ u0 C6 A0 [
DWORD s_time = GetTickCount(); //获取系统时间(从操作系统运行开始到当前的时间),第一次计时1 w4 A2 m1 M! W. N
while ( nLeft > 0 )7 m+ ^8 l" C$ e( F2 B( {
{( Y2 Z: v4 O) Z4 {9 V9 b8 P8 l
MSG msg;. R! C3 ]: D. B+ I) H5 N2 v
PeekMessage(&msg, NULL, 0, 0, PM_REMOVE) ;
" n# i* y; c" V- V! Xif(msg.message == WM_QUIT)" g6 h# K8 P9 B& B# \8 b
{' m, X8 F6 I S% h" T- y; r2 q8 U/ `- k
return 0;
4 T& a% o! w3 t' B. K$ [9 C }
+ a6 l+ @, ~% ^: i9 M% p5 V# Q, t; B2 ?& r
FD_ZERO( &readfds ); //每次循环更新文件描述符
$ Y( B0 o0 \, i2 D1 f FD_SET( sock , &readfds );1 k) w, c! m) F/ G+ i! `/ i: s
, Q2 H2 R, b5 q. x2 c/ V- E! hint errorret = select( 0 , NULL, &readfds, NULL , &timeout ); //时间阻塞式监控,检测套接字是否可写. E5 i' X* [. _ H: }* C# u
# g, Y( d# d$ i. _5 s/ _; I; I
if( errorret == SOCKET_ERROR )
3 |3 d8 x/ U2 i X- |7 z- x {
4 T2 b4 Y+ K% W0 y) U( b( j# T// AfxMessageBox("mysendEx SOCKET 错误");
( |4 S& v4 M9 |& k5 G1 E# Areturn SOCKET_ERROR;
3 w& T# u) v8 N$ L5 O! G6 s% k" e }
A4 \, r- a1 v2 F( r" ?! R' F) E
& d @$ Q* j1 W DWORD e_time = GetTickCount( ); //第二次计时
, a/ w4 R: H3 \if ( !FD_ISSET( sock , &readfds ) ) //检测是否可以发送,如果为否表示正在占用! e/ v( U, y1 q; j5 g
{/ A9 v' K% ^9 z& n9 s/ a
: |" g3 ?( ]( U% ~if( e_time - s_time > overtime*1000 ) //检测时间窗口是否超时
" v& k' G& U9 v9 e$ t. f7 X {
5 n M, U+ |0 m$ s) D// AfxMessageBox("mysendEx发送数据超时"); J& e+ a& R# b2 P* l
return 0;. s2 m4 ~# I( U8 ?/ |
}
$ ]7 P5 Q _, F0 t; \+ zelse N- S: ]' W# c- k2 o) k8 m! d( ?% N' E* m
{
2 y2 T: ~! K9 D' y5 Xcontinue;
8 d9 r0 }5 @$ I- U, E2 e2 z8 f: M }' u/ I8 [) G8 V8 ?- S6 o' n9 p
}
( _ L8 m V6 w% h/ u8 p5 @/ v9 M7 l% M( s- v6 ?
ret = send( sock, &buf[idx], nLeft, flag ); //返回实际发送的字节数5 p- k' k0 @* \
: h) ^% \* g9 q( t1 H( Dif ( ret <= 0 )
3 a1 r( q, q4 ?6 ^; U {
1 H$ R0 K' D2 Oreturn ret;
" m: p8 k; X( ~: L* V3 n }
7 ]0 j& c9 l& f, y( y
! \7 Y* P7 j9 s/ {% A" e nLeft -= ret; //剩余字节数-, H( c; ~( P! N9 X, c* y3 p) l
idx += ret; //索引值+
+ Q, B) x& _% T8 ?% h) `/ I9 ]! X1 L+ X7 _. K8 }3 P
}& G, i) D8 z4 V( ^# |
return len; //返回发送字节数. o5 Q5 h: K% J3 J4 w
}myrecv函数myrecv函数用来接收指定的套接字数据。sock为接收端套接字描述符。buf 用来存放接收到的数据的缓冲区。len为接收数据的缓冲区的大小。flag一般设置为0。overtime为超时时间。endmark为结束标记。soonflag为是否立即返回结果,默认为否。与mysend函数一样采用select机制防止I/O操作阻塞。int CTcpTran::myrecv(SOCKET sock, char *buf, int len, int flag , int overtime ,char*EndMark,BOOL soonflag)# x8 g d; U# `' G
{
7 S4 n, b/ h, u/ r; N( Eint ret;+ {- h8 ?0 F9 z$ Z) L. P
int nLeft = len;
+ v' Z5 O) ^: H! Q2 E) gint idx = 0;1 b* n0 c- ~' M8 A0 ~: H6 ~5 I' l
int nCount = 0;! G; W- P* [/ q5 x1 x
fd_set readfds;
& \! U0 C/ ~* C struct timeval timeout;
( z P X$ w0 v/ Z timeout.tv_sec = 0;; Z, t* w2 }9 R2 D
timeout.tv_usec = 500;
4 h0 _ N4 E/ h3 f, {% z/ ] DWORD s_time = GetTickCount();
4 J0 t; J$ _- i7 c! \! k
" U5 n* a {4 z0 I$ L3 C8 Y- ~while ( nLeft > 0 )- a" h: q1 @/ q% v' R/ d& L
{% f. U% p) a2 m- R
//接收消息' l& {6 S/ J1 u
MSG msg;, B$ P$ a! n* i! @$ H' w5 E; \
PeekMessage(&msg, NULL, 0, 0, PM_REMOVE) ;
$ h% b) g- O h& ~if(msg.message == WM_QUIT)
4 Y, }" ]' N( V' V6 C! Breturn 0;9 [+ t% o0 E# S. U8 O
4 W- Q3 v: T \6 ]4 @ FD_ZERO( &readfds );9 d2 A& t% n( |# g
FD_SET( sock , &readfds );: q* Y1 B3 U3 l* k3 Z# y# Q
if( select( 0 , &readfds , NULL , NULL , &timeout ) == SOCKET_ERROR ) e1 ^, i. b, N0 Y# ~7 D
{
" C1 e( @- L. h& I0 j// AfxMessageBox("recv SOCKET 错误");
" y, s# ?2 A( |: h( Sreturn SOCKET_ERROR;
' ]' z, D% } F' `8 B9 p6 P }1 u8 R5 q. q) @% o
: U4 ~$ q2 o, v& N( r
DWORD e_time = GetTickCount( );9 t) M* v4 q \1 ?6 W9 o
if ( !FD_ISSET( sock , &readfds ) )+ k" f4 F! T" z2 }7 `, p( j
{
: J% V' P. J& C6 q1 p7 c" Oif( e_time - s_time > overtime*1000 ) 0 l3 N3 u2 W" J/ [3 E
{ E7 D i7 R* V+ i$ w4 D; M+ q
// AfxMessageBox("recv SOCKET 超时");
! ~: X8 x+ [& E. U/ i4 j3 ?9 hreturn SOCKET_TIMEOUT;5 W/ P$ |0 v! d
}
6 j1 z& j7 }8 d0 E: |else& X! m% C/ e6 P- i
continue;
9 V+ e+ X3 r6 e- W+ Q% ]' D }
! C; Y/ X% x2 K8 q( s4 l
6 G8 R/ u' D }4 ]0 ]' v& Y ret = recv( sock, &buf[idx], nLeft, flag );& [/ q$ c+ H' _& r
if( soonflag == TRUE )$ G) f. s3 H2 C
{
& l3 S5 W! s5 u: k1 Z3 s' k8 Vreturn ret;
7 [2 [: ?1 T/ O) r6 T }4 i8 v4 [8 l, w% }9 I, T' y) A
, U" R& y% |3 E$ H$ t& ? s_time = e_time ; // 只要有数据就重新置初始时间值
6 h* G1 J6 {4 E( e- t- y5 @2 i8 }/ i5 ~$ K7 L1 h
if ( ret <= 0 )6 ~7 K9 W8 j: W4 v3 ]
{
. K! A0 E9 G7 m: V/ n! K) F) D; K' `int LastError = GetLastError();
1 Y- t/ C& S( Sif ( ( -1 == ret ) && ( WSAETIMEDOUT == LastError ) ), _, F8 l+ @( E1 ~! {) e5 y
continue; P6 } {8 \, Y
if ( ( -1 == ret ) && ( WSAEWOULDBLOCK == LastError ) )
. O3 B; a' P7 O {$ y8 U0 ?: y2 ^- c% W! u4 t8 _
if ( nCount < 2000 )- p; i- Q. T; _' G' `1 S, p3 |8 ~; X
{. t, T6 R; }, C! `
Sleep( 10 );
% J: ?# g( D6 M5 G( u nCount++;
$ a' O- I y9 o- h! a( _& Kcontinue;. o1 l) d) B5 D, V1 ?* Y0 |" O \* J
}5 M. ~* O1 _- R- r$ s+ K+ ^" g
}
! r% I* C# p/ [8 X/ L* J" e2 `return ret; P7 _7 p- V! z9 \
}
9 o/ F$ B- |5 Y" T. K/ g nCount = 0;
3 p, p* M# Q" C/ Q; H/ |* J% j* J# }7 K: H* @$ X8 h x6 A2 L! B
nLeft -= ret;. l$ a# U# R, ^3 {0 x' H
idx += ret;; n3 o, [* G& |) E8 f/ a
* V6 [, T; `7 e/ Y/ x+ S
if( EndMark != NULL && idx>5)) m8 n) L, E$ ]- q
{
" F g- v4 l+ ]3 H( ]4 L2 aif( strstr(buf+(idx-5),EndMark) != NULL )
% D6 V0 d6 D: A* ]& } j {
1 G: a& [' u- W9 ?* o6 Fbreak;& b7 n7 J4 Z/ \/ @$ A7 N/ |& v
}
+ F1 ?/ I% |" l# ` ] }/ ]& _( i; }7 Q- Y! b5 E
}
$ V1 O9 f7 n" n. X6 j% X' |4 ^, P% d# O3 A! b3 q$ O% V; y
return idx;0 t' r3 K; ?5 j- e+ n. ~2 ?. v% e- s3 Y
}" s7 B4 u ], A9 n* D/ _9 _3 r
主界面 功能界面进程管理 文件管理 服务管理 远程SHELL 远程桌面 |
|