|
|
采用成熟的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)
* Z! }# h& M4 Z5 k$ L$ z e{
w- z: b! t, T- m SOCKET socketid = INVALID_SOCKET;
: ?0 H* g$ w1 u; Q- W" y socketid = socket(PF_INET,SOCK_STREAM,0); //建立一个流式套接字句柄
8 t/ U; T: m" ?% I SOCKADDR_IN sockStruct; //初始化一个地址结构
" @$ v6 K/ W3 v! M3 b sockStruct.sin_family = AF_INET; //使用TCP/IP协议
: W6 b4 _7 |# q4 x5 W; A7 T/ p& X3 c9 X3 A- x+ ^
if( strBindIp.empty() )8 u0 V7 M1 q: B' C, U
{
, I& R8 Q" p0 W$ O: X; m sockStruct.sin_addr.S_un.S_addr = INADDR_ANY; //如果strBindIp为空,则为本地任意地址
, V: x' ^) s) Q' h- N0 m4 m) d9 S5 `$ B- O+ `: Q+ Y
}else
% m% g6 }1 P; }7 ~0 Y {/ u5 ^6 O V/ J8 P
sockStruct.sin_addr.S_un.S_addr = inet_addr(strBindIp.c_str());
: [& n) J$ U; d9 U; M: J$ H }
2 B4 v2 C7 p5 n/ f
$ W- o' s) H. v8 r& [9 G" p( a6 x4 M! `# M- T0 n
sockStruct.sin_port = htons(BindPort); //转换为网络字节! ?8 j" ^6 |- ^+ |
2 B4 H! K% x( B$ W6 ~
if( SocketType == SOCKETNOBIND )
3 T# j& f2 @! j5 y {9 _' l/ x: W( V5 K0 x
if(connect(socketid,(LPSOCKADDR)&sockStruct,sizeof(sockStruct)) == SOCKET_ERROR) //不绑定,直接连接,被控端选择非绑定方式连接1 e% o+ Y9 R0 H' x. o% w# V
{9 j- A9 c9 J% t, j6 I
// AfxMessageBox("InitSocket 错误");
& m6 U) T- b) @& [( B* y2 p+ Q closesocket(socketid);3 @8 b" F- ^: M) v9 \
shutdown(socketid,2);
# y+ |8 [# [3 G9 h+ k. b socketid = INVALID_SOCKET;
* j5 \3 ]' @9 y8 A1 Y* u2 r$ Y4 @% Y }
' h1 b) g! M s9 E
+ [: u c- N- [/ K/ G6 u m_Socket = socketid;
* ]( @) t& L, m) [
1 k' c6 ?2 X8 ?7 G3 k }else if( SocketType == SOCKETBIND ) //控制端选择绑定本地端口$ {2 S+ w( h7 }. c& w) U3 J
{* A; K7 M& r/ D/ R3 V6 q, u
if(bind(socketid,(sockaddr*)&sockStruct,sizeof(sockaddr_in)) == SOCKET_ERROR) //绑定地址结构0 U4 a6 A$ L3 Q. m
{/ V4 h" i5 t8 b/ U
closesocket(socketid);
/ l5 j4 `& s% s* b socketid = INVALID_SOCKET;
4 @! Y) c! Y) n1 M9 n9 G( J; k6 D7 _8 q& O" n8 J' {9 B P" V
}else
% ~/ }5 N; z9 z {
" h U/ w; D6 V" @if( listen(socketid,SOMAXCONN) == SOCKET_ERROR ) //进入监听
! n' h' ]4 _9 ^0 s1 j1 F A8 N8 b {& B# S9 D. X: \7 r4 N2 t( T! }0 k
closesocket(socketid);* z- n5 O1 u" c
socketid = INVALID_SOCKET;8 k0 m5 w1 X& _: R4 U
}
6 K9 Y! Z9 j9 F2 o2 F6 g5 a }/ q9 T1 p0 P) I. C" v3 [; }
" x! p# J2 N5 E' M
m_Socket = socketid;4 Z0 V1 B" J, s7 h \% q
}1 }: g% }, h, D, u% u
) L& k# Q" x" U/ K/ O/ G2 x- g# _4 Wreturn socketid; //返回建立的socket
, O6 J" ^0 T$ J3 Q} myaccept函数服务器接收客户端的连接请求,创建一个新的套接字和参数addr指定的客户端套接字建立连接通道。s表示处于监听状态的流套接字。addr表示新创建的套接字地址结构。addrlen表示新创建套接字的地址结构的长度。SOCKET CTcpTran::myaccept(SOCKET s,struct sockaddr* addr,int* addrlen)
6 D) A/ ~; n- n* r. H: h8 b* [2 l{5 w7 o+ F+ a; u
SOCKET accpsocket = INVALID_SOCKET;
8 W/ a+ ?/ l: t# q0 N* M4 y8 y1 W accpsocket = accept(s,addr,addrlen);1 z; N$ z' U% o+ d
return accpsocket;
5 ^; e+ x5 v; f7 j, ~}
4 h p: z7 P8 y) D2 `) @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)) P9 ~% J! x3 q
{
: E( h! i6 F9 Q9 {* m+ u- Nint ret;4 F8 Y# n* [5 E1 y+ u
int nLeft = len; //待发送的字节数6 G! I1 C# t) E h- h/ p4 `; x
int idx = 0; //发送缓冲区索引 ^: J4 e: { q. c2 V5 K
$ }6 ]' L7 j+ c2 f* B fd_set readfds;
4 d. n# D; O( { struct timeval timeout; 9 o- K+ c6 k' o6 w* z
timeout.tv_sec = 0;
8 j7 I$ `' `& \2 `% W. ^& R5 f% T1 H timeout.tv_usec = 500; : e; Q, }( C+ o8 D+ W* D2 ~& }7 E+ _
DWORD s_time = GetTickCount(); //获取系统时间(从操作系统运行开始到当前的时间),第一次计时
; n+ ~0 L( u: Z5 zwhile ( nLeft > 0 )
4 v; V2 x+ I; e% u! v2 d* ? {8 A3 B! H! t7 x. P9 B8 g$ X
MSG msg;% M; I7 F3 X0 B% \' |
PeekMessage(&msg, NULL, 0, 0, PM_REMOVE) ;& N* p0 h, @% F& M
if(msg.message == WM_QUIT)
8 u# |$ Y8 S4 b! V1 ^ {
5 k1 @& d( {$ S8 @+ d$ N0 Mreturn 0;3 v* W. g& K9 J1 @1 J' j" [
}
' H: v" n) f2 ^0 E7 F3 R0 `# {0 L' \ d& o' H4 G5 P
FD_ZERO( &readfds ); //每次循环更新文件描述符
6 W, |0 ^4 ?; }# H, V FD_SET( sock , &readfds );
1 I$ z: a6 u9 Z# }, X
1 s2 R4 `; `6 Q5 b* Rint errorret = select( 0 , NULL, &readfds, NULL , &timeout ); //时间阻塞式监控,检测套接字是否可写8 \0 P5 M1 D9 U5 U1 S
# u- s/ N$ Q6 ~& zif( errorret == SOCKET_ERROR )( f) C: b) k! l: h: |
{
% y* I \) w- y0 M// AfxMessageBox("mysendEx SOCKET 错误");2 u8 h2 @2 @+ Y/ t& `3 G
return SOCKET_ERROR;
5 S9 T) ^4 G) O( x- f5 q }# q- N, l- P+ E# g1 q9 q! d% j
9 r9 d$ N7 ?: R) V& T- x
DWORD e_time = GetTickCount( ); //第二次计时9 G" x( |% t) Y; T
if ( !FD_ISSET( sock , &readfds ) ) //检测是否可以发送,如果为否表示正在占用
) H$ g8 D; |3 @$ I: \, N5 j {# @% v- U' G6 v8 w% E0 A5 I
* a6 B; z$ s a1 M3 v& N- M& e8 Uif( e_time - s_time > overtime*1000 ) //检测时间窗口是否超时
3 j( a5 E6 F- s( D; _. t" ^6 o {1 I4 R0 n, x& ?1 O" H) ~8 A
// AfxMessageBox("mysendEx发送数据超时"); U5 E& X: V- i: \( p
return 0;- w7 w* @3 T( K$ o
}! D3 o0 G* i# `" K
else
' c7 T: e+ X$ E7 c' { {3 o( x' {' v' Q$ _; Y
continue;
, |/ g# r! R2 V* z. A5 G }% t3 y9 L( s; @$ v
}
, J" s6 W1 t0 Z) y8 O% s: u0 h M, _" X5 Q
ret = send( sock, &buf[idx], nLeft, flag ); //返回实际发送的字节数
* P) \9 d* q2 s- B% [1 }+ M8 O" x
$ u; @* S! K1 J* T& C) ~if ( ret <= 0 )! J2 j; W2 A6 l$ e
{8 F2 Z% U# }6 V5 h% F$ Y; m' X
return ret;
- T0 Z% o* e% G }7 U3 Q+ z. o( X
* }0 g7 G$ P* G1 Q6 q
nLeft -= ret; //剩余字节数-
6 s" t1 h6 i+ w7 j4 j' R% R7 n7 @4 |8 [ idx += ret; //索引值+
. t: a# l6 ?; |; @3 Q8 W+ [& d; a6 a& G
}# v9 g0 R, M; r
return len; //返回发送字节数6 r) K$ i& J1 `6 Y, C, m
}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)( @3 D& |- F# l
{
8 x3 b* f# y% T2 v% ^int ret;
' R* L: m# Z1 A5 P( Gint nLeft = len;
I4 {( t/ j, H$ r: U: mint idx = 0;
+ P: F* \* }0 t+ c, v8 `int nCount = 0;
7 n, j8 }; G l3 p9 n4 l fd_set readfds;, G L6 a& f" S* H6 G
struct timeval timeout;' m) g$ E6 i" K. W8 P
timeout.tv_sec = 0;7 h, e0 X; }7 n; d( i' f
timeout.tv_usec = 500;
' ^ M G. n/ u) E0 u- V DWORD s_time = GetTickCount();8 m# ^ ]. T/ H1 Y: W9 R
" [9 U( c( I/ [( d' |% w, h$ V$ [
while ( nLeft > 0 )2 ] ]. E; ?' d* |
{
& K. A; V% `; ~//接收消息/ B/ [% l5 Y- Q( h8 f9 ^
MSG msg;/ n3 A5 T' R2 Z% L' ^' w
PeekMessage(&msg, NULL, 0, 0, PM_REMOVE) ;
8 e( `( l1 J. `+ d" f# u% p5 n5 G! fif(msg.message == WM_QUIT)
" R- H, i4 j, v1 l4 K2 o, V1 @6 Preturn 0;
2 ^- I3 Q i3 L) g! E/ W3 x) ~4 u( U% y* ?
FD_ZERO( &readfds );4 i1 p: k+ T& [4 Z" L* q
FD_SET( sock , &readfds );' L; ? w* T" i6 _6 D
if( select( 0 , &readfds , NULL , NULL , &timeout ) == SOCKET_ERROR )
; Q; }( o; a3 {$ N/ R6 M {
& `5 z8 T! R, k// AfxMessageBox("recv SOCKET 错误");; b: X7 W) l: v6 Q( U% o: ?
return SOCKET_ERROR;2 d6 _2 O, K' R9 `7 B/ P
}
9 r, ] ~2 Q* @" c+ O* W, k. {# d! h6 B
DWORD e_time = GetTickCount( );
( U. T! Z. \) F- ~) k' |if ( !FD_ISSET( sock , &readfds ) )
: ~3 n, f7 e3 t% g {
1 a: m2 [+ A/ L ?% S7 r, gif( e_time - s_time > overtime*1000 ) ) d9 Q$ P& }; G/ v7 ~% r% ?
{# B/ E- t. g6 h5 f' V9 {3 T' b
// AfxMessageBox("recv SOCKET 超时");
( N }$ c" w6 f& g: o8 w3 _$ Sreturn SOCKET_TIMEOUT;) z. ~' X0 }8 L g+ u1 P8 B3 U/ g
}
3 R4 J: a5 v d7 m) x7 d& welse
7 J8 B5 y; m1 d" t/ scontinue;
4 J. J1 H1 G* _5 V% B, N }
8 ^* l$ j" u1 r! _! H, d( J( T9 { l) Z s7 _& p
ret = recv( sock, &buf[idx], nLeft, flag );
6 \) ?# Q1 ~7 |# }if( soonflag == TRUE )
- Y* E; T; X, N& p1 a n0 e$ K. q9 A {
$ x( ]7 o$ G( Y# f2 O( `* _! ?5 Jreturn ret;' G2 c+ E& L( P& S9 I `' H
}* c4 O! C/ {1 J/ R* v% j
5 i u. D" o" ?* W
s_time = e_time ; // 只要有数据就重新置初始时间值
. r. P! }8 N5 s- b' U; n0 `
0 g- d- J+ k; ~# r" m9 Yif ( ret <= 0 )
/ h7 Z* z; H$ X& r: w {
8 J$ G: S8 |: ]& Y2 L( p. eint LastError = GetLastError();7 z. F) y% m9 }
if ( ( -1 == ret ) && ( WSAETIMEDOUT == LastError ) )
0 U' H& Q7 L2 P! ucontinue;
, v; r* e) o8 p# tif ( ( -1 == ret ) && ( WSAEWOULDBLOCK == LastError ) )8 U5 n4 A/ R* c) m; S2 o5 `, n8 D
{
& W( k" i3 V3 K0 j9 }' cif ( nCount < 2000 ). F! o3 x2 _. c; p, j
{
1 k6 s0 q3 T" F! C; n- s3 h Sleep( 10 );( A& b! B* h: j: ^9 t1 [
nCount++;
8 F/ k. T3 D l( _0 z* D2 W6 v M3 g( Scontinue;$ N" C. y$ O/ z0 u4 q
}3 _$ G2 R" T2 O7 H& W
}. G/ b, Z* a& U. h6 P( p
return ret;
6 |2 C5 ?2 A2 }6 y7 R }: H" x6 k, q0 U& h, `
nCount = 0;
6 D$ q) P# v) C+ S' ^, D. J8 }% l
- H9 e' _( y1 v2 l4 m6 O nLeft -= ret;
0 X8 z& j2 h$ f6 \ idx += ret;
% ]0 w4 E6 r" n
& j9 l* h7 w" Q7 l( L3 M3 D( C* o# Dif( EndMark != NULL && idx>5)
' h# e8 P8 _1 O( ^; t' D% Q. V% @ {3 }/ ?/ o3 S# T( d1 i& b
if( strstr(buf+(idx-5),EndMark) != NULL )# E. p+ J, F2 [& T9 G( x& E) T, D
{
! G+ i7 t2 Z: B! ?+ _' W" ^' Ybreak;$ w* E4 j* K) r% F. F
}
) Z7 J+ Y4 j0 I6 h }
% ]4 y" e& ^$ I+ m& e }. Y5 O8 p5 h, }7 s2 I0 Z2 Q/ @
/ p' N$ i( G' Xreturn idx;
5 l, o! g T( E% g: ]}6 h X3 r5 k8 B
主界面 功能界面进程管理 文件管理 服务管理 远程SHELL 远程桌面 |
|